diff options
Diffstat (limited to 'lib/kernel/test')
99 files changed, 29119 insertions, 19795 deletions
diff --git a/lib/kernel/test/Makefile b/lib/kernel/test/Makefile index ef351a25fb..4a86265a4a 100644 --- a/lib/kernel/test/Makefile +++ b/lib/kernel/test/Makefile @@ -1,18 +1,19 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1997-2013. All Rights Reserved. +# Copyright Ericsson AB 1997-2018. 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/. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# 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. +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # # %CopyrightEnd% # @@ -69,6 +70,15 @@ MODULES= \ interactive_shell_SUITE \ init_SUITE \ kernel_config_SUITE \ + logger_SUITE \ + logger_disk_log_h_SUITE \ + logger_env_var_SUITE \ + logger_filters_SUITE \ + logger_formatter_SUITE \ + logger_legacy_SUITE \ + logger_simple_h_SUITE \ + logger_std_h_SUITE \ + logger_test_lib \ os_SUITE \ pg2_SUITE \ seq_trace_SUITE \ @@ -78,7 +88,9 @@ MODULES= \ zlib_SUITE \ loose_node \ sendfile_SUITE \ - standard_error_SUITE + standard_error_SUITE \ + multi_load_SUITE \ + zzz_SUITE APP_FILES = \ appinc.app \ @@ -99,7 +111,7 @@ TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR)) INSTALL_PROGS= $(TARGET_FILES) EMAKEFILE=Emakefile -COVERFILE=kernel.cover +COVERFILE=kernel.cover logger.cover # ---------------------------------------------------- # Release directory specification @@ -111,7 +123,7 @@ RELSYSDIR = $(RELEASE_PATH)/kernel_test # ---------------------------------------------------- ERL_MAKE_FLAGS += -ERL_COMPILE_FLAGS += -I$(ERL_TOP)/lib/test_server/include +ERL_COMPILE_FLAGS += EBIN = . @@ -146,8 +158,9 @@ release_tests_spec: make_emakefile $(INSTALL_DIR) "$(RELSYSDIR)" $(INSTALL_DATA) $(ERL_FILES) "$(RELSYSDIR)" $(INSTALL_DATA) $(APP_FILES) "$(RELSYSDIR)" - $(INSTALL_DATA) kernel.spec kernel_smoke.spec $(EMAKEFILE)\ - $(COVERFILE) "$(RELSYSDIR)" + $(INSTALL_DATA) \ + kernel.spec kernel_smoke.spec kernel_bench.spec logger.spec \ + $(EMAKEFILE) $(COVERFILE) "$(RELSYSDIR)" chmod -R u+w "$(RELSYSDIR)" @tar cf - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -) diff --git a/lib/kernel/test/appinc1.erl b/lib/kernel/test/appinc1.erl index 343fefb25c..b571208834 100644 --- a/lib/kernel/test/appinc1.erl +++ b/lib/kernel/test/appinc1.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2010. All Rights Reserved. +%% Copyright Ericsson AB 1998-2016. 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/lib/kernel/test/appinc1x.erl b/lib/kernel/test/appinc1x.erl index 8c144676ac..3aa6f3dcb9 100644 --- a/lib/kernel/test/appinc1x.erl +++ b/lib/kernel/test/appinc1x.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2010. All Rights Reserved. +%% Copyright Ericsson AB 1998-2016. 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/lib/kernel/test/appinc2.erl b/lib/kernel/test/appinc2.erl index d2e0305109..a665e628a2 100644 --- a/lib/kernel/test/appinc2.erl +++ b/lib/kernel/test/appinc2.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2010. All Rights Reserved. +%% Copyright Ericsson AB 1998-2016. 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/lib/kernel/test/appinc2A.erl b/lib/kernel/test/appinc2A.erl index 604e31e3d3..378eb179f2 100644 --- a/lib/kernel/test/appinc2A.erl +++ b/lib/kernel/test/appinc2A.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2010. All Rights Reserved. +%% Copyright Ericsson AB 1998-2016. 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/lib/kernel/test/appinc2B.erl b/lib/kernel/test/appinc2B.erl index abb60010aa..35b7016906 100644 --- a/lib/kernel/test/appinc2B.erl +++ b/lib/kernel/test/appinc2B.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2010. All Rights Reserved. +%% Copyright Ericsson AB 1998-2016. 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/lib/kernel/test/appinc2top.erl b/lib/kernel/test/appinc2top.erl index 5a8d0d6687..3b6dc4ea31 100644 --- a/lib/kernel/test/appinc2top.erl +++ b/lib/kernel/test/appinc2top.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2010. All Rights Reserved. +%% Copyright Ericsson AB 1998-2016. 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/lib/kernel/test/application_SUITE.erl b/lib/kernel/test/application_SUITE.erl index 4901206c8e..5c35b82207 100644 --- a/lib/kernel/test/application_SUITE.erl +++ b/lib/kernel/test/application_SUITE.erl @@ -1,24 +1,25 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2014. All Rights Reserved. +%% Copyright Ericsson AB 1996-2018. 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% -module(application_SUITE). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2 @@ -36,17 +37,18 @@ -export([config_change/1, persistent_env/1, distr_changed_tc1/1, distr_changed_tc2/1, ensure_started/1, ensure_all_started/1, - shutdown_func/1, do_shutdown/1, shutdown_timeout/1, shutdown_deadlock/1]). + shutdown_func/1, do_shutdown/1, shutdown_timeout/1, shutdown_deadlock/1, + config_relative_paths/1]). -define(TESTCASE, testcase_name). --define(testcase, ?config(?TESTCASE, Config)). +-define(testcase, proplists:get_value(?TESTCASE, Config)). -export([init_per_testcase/2, end_per_testcase/2, start_type/0, start_phase/0, conf_change/0]). -% Default timetrap timeout (set in init_per_testcase). --define(default_timeout, ?t:minutes(2)). -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap,{minutes,2}}]. all() -> [failover, failover_comp, permissions, load, @@ -54,7 +56,7 @@ all() -> script_start, nodedown_start, permit_false_start_local, permit_false_start_dist, get_key, get_env, ensure_all_started, {group, distr_changed}, config_change, shutdown_func, shutdown_timeout, - shutdown_deadlock, + shutdown_deadlock, config_relative_paths, persistent_env]. groups() -> @@ -80,21 +82,15 @@ end_per_group(_GroupName, Config) -> init_per_testcase(otp_2973=Case, Config) -> - code:add_path(?config(data_dir,Config)), - Dog = test_server:timetrap(?default_timeout), - [{?TESTCASE, Case}, {watchdog, Dog}|Config]; + code:add_path(proplists:get_value(data_dir,Config)), + [{?TESTCASE, Case}|Config]; init_per_testcase(Case, Config) -> - Dog = test_server:timetrap(?default_timeout), - [{?TESTCASE, Case}, {watchdog, Dog}|Config]. + [{?TESTCASE, Case}|Config]. end_per_testcase(otp_2973, Config) -> - code:del_path(?config(data_dir,Config)), - Dog=?config(watchdog, Config), - test_server:timetrap_cancel(Dog), + code:del_path(proplists:get_value(data_dir,Config)), ok; -end_per_testcase(_Case, Config) -> - Dog=?config(watchdog, Config), - test_server:timetrap_cancel(Dog), +end_per_testcase(_Case, _Config) -> ok. -define(UNTIL(Seq), loop_until_true(fun() -> Seq end)). @@ -119,10 +115,8 @@ loop_until_true(Fun) -> %% Should be started in a CC view with: %% erl -sname XXX -rsh ctrsh where XX not in [cp1, cp2, cp3] %%----------------------------------------------------------------- -failover(suite) -> []; -failover(doc) -> - ["Tests failover and takeover for distributed applications. Tests", - "start, load etc implicitly."]; +%% Tests failover and takeover for distributed applications. Tests +%% start, load etc implicitly. failover(Conf) when is_list(Conf) -> %% start a help process to check the start type StPid = spawn_link(?MODULE, start_type, []), @@ -132,14 +126,14 @@ failover(Conf) when is_list(Conf) -> NoSyncTime = config_fun_fast(config_fo(NodeNames)), WithSyncTime = config_fun(config_fo(NodeNames)), - % Test [cp1, cp2, cp3] + %% Test [cp1, cp2, cp3] {ok, Cp1} = start_node_config(Ncp1, NoSyncTime, Conf), {ok, Cp2} = start_node_config(Ncp2, NoSyncTime, Conf), {ok, Cp3} = start_node_config(Ncp3, WithSyncTime, Conf), Cps = [Cp1, Cp2, Cp3], wait_for_ready_net(), - % Start app1 and make sure cp1 starts it + %% Start app1 and make sure cp1 starts it {[ok,ok,ok],[]} = rpc:multicall(Cps, application, load, [app1()]), ?UNTIL(is_loaded(app1, Cps)), @@ -149,12 +143,12 @@ failover(Conf) when is_list(Conf) -> false = is_started(app1, Cp2), ok = get_start_type(#st{normal = 3}), - % Stop cp1 and make sure cp2 starts app1 + %% Stop cp1 and make sure cp2 starts app1 stop_node_nice(Cp1), ?UNTIL(is_started(app1, Cp2)), ok = get_start_type(#st{normal = 3}), - % Restart cp1 and make sure it restarts app1 + %% Restart cp1 and make sure it restarts app1 {ok, Cp1_2} = start_node_config(Ncp1, NoSyncTime, Conf), global:sync(), ok = rpc:call(Cp1_2, application, load, [app1()]), @@ -163,8 +157,8 @@ failover(Conf) when is_list(Conf) -> ?UNTIL(not is_started(app1, Cp2)), ok = get_start_type(#st{takeover = 3}), - % Test [{cp1, cp2}, cp3] - % Start app_sp and make sure cp2 starts it (cp1 has more apps started) + %% Test [{cp1, cp2}, cp3] + %% Start app_sp and make sure cp2 starts it (cp1 has more apps started) {[ok,ok,ok],[]} = rpc:multicall([Cp1_2, Cp2, Cp3], application, load, [app_sp()]), {[ok,ok,ok],[]} = @@ -174,17 +168,17 @@ failover(Conf) when is_list(Conf) -> false = is_started(app_sp, Cp3), ok = get_start_type(#st{normal = 3}), - % Stop cp2 and make sure cp1 starts app_sp + %% Stop cp2 and make sure cp1 starts app_sp stop_node_nice(Cp2), ?UNTIL(is_started(app_sp, Cp1_2)), ok = get_start_type(#st{failover = 3}), - % Stop cp1 and make sure cp3 starts app_sp + %% Stop cp1 and make sure cp3 starts app_sp stop_node_nice(Cp1_2), ?UNTIL(is_started(app_sp, Cp3)), ok = get_start_type(#st{normal = 3, failover = 3}), - % Restart cp2 and make sure it restarts app_sp + %% Restart cp2 and make sure it restarts app_sp {ok, Cp2_2} = start_node_config(Ncp2, NoSyncTime, Conf), global:sync(), ok = rpc:call(Cp2_2, application, load, [app_sp()]), @@ -193,16 +187,16 @@ failover(Conf) when is_list(Conf) -> ?UNTIL(not is_started(app_sp, Cp3)), ok = get_start_type(#st{takeover = 3}), - % Restart cp1 and make sure it doesn't restart app_sp + %% Restart cp1 and make sure it doesn't restart app_sp {ok, Cp1_3} = start_node_config(Ncp1, NoSyncTime, Conf), global:sync(), ok = rpc:call(Cp1_3, application, load, [app_sp()]), ok = rpc:call(Cp1_3, application, start, [app_sp, permanent]), - test_server:sleep(500), + ct:sleep(500), false = is_started(app_sp, Cp1_3), true = is_started(app_sp, Cp2_2), - % Force takeover to cp1 + %% Force takeover to cp1 ok = rpc:call(Cp1_3, application, takeover, [app_sp, permanent]), ?UNTIL(is_started(app_sp, Cp1_3)), ?UNTIL(not is_started(app_sp, Cp2_2)), @@ -224,11 +218,9 @@ failover(Conf) when is_list(Conf) -> %% Should be started in a CC view with: %% erl -sname XXX -rsh ctrsh where XX not in [cp1, cp2, cp3] %%----------------------------------------------------------------- -failover_comp(suite) -> []; -failover_comp(doc) -> - ["Tests failover and takeover for distributed applications. Tests", - "start, load etc implicitly. The applications do not use start_phases," - "i.e the failover should be trasfered to normal start type."]; +%% Tests failover and takeover for distributed applications. Tests +%% start, load etc implicitly. The applications do not use start_phases +%% i.e. the failover should be transfered to normal start type. failover_comp(Conf) when is_list(Conf) -> %% start a help process to check the start type StPid = spawn_link(?MODULE, start_type, []), @@ -238,14 +230,14 @@ failover_comp(Conf) when is_list(Conf) -> NoSyncTime = config_fun_fast(config(NodeNames)), WithSyncTime = config_fun(config(NodeNames)), - % Test [cp1, cp2, cp3] + %% Test [cp1, cp2, cp3] {ok, Cp1} = start_node_config(Ncp1, NoSyncTime, Conf), {ok, Cp2} = start_node_config(Ncp2, NoSyncTime, Conf), {ok, Cp3} = start_node_config(Ncp3, WithSyncTime, Conf), Cps = [Cp1, Cp2, Cp3], wait_for_ready_net(), - % Start app1 and make sure cp1 starts it + %% Start app1 and make sure cp1 starts it {[ok,ok,ok],[]} = rpc:multicall(Cps, application, load, [app1()]), ?UNTIL(is_loaded(app1, Cps)), @@ -255,12 +247,12 @@ failover_comp(Conf) when is_list(Conf) -> false = is_started(app1, Cp2), ok = get_start_type(#st{normal = 3}), - % Stop cp1 and make sure cp2 starts app1 + %% Stop cp1 and make sure cp2 starts app1 stop_node_nice(Cp1), ?UNTIL(is_started(app1, Cp2)), ok = get_start_type(#st{normal = 3}), - % Restart cp1 and make sure it restarts app1 + %% Restart cp1 and make sure it restarts app1 {ok, Cp1_2} = start_node_config(Ncp1, NoSyncTime, Conf), global:sync(), ok = rpc:call(Cp1_2, application, load, [app1()]), @@ -270,8 +262,8 @@ failover_comp(Conf) when is_list(Conf) -> ?UNTIL(not is_started(app1, Cp2)), ok = get_start_type(#st{takeover = 3}), - % Test [{cp1, cp2}, cp3] - % Start app3 and make sure cp2 starts it (cp1 has more apps started) + %% Test [{cp1, cp2}, cp3] + %% Start app3 and make sure cp2 starts it (cp1 has more apps started) {[ok,ok,ok],[]} = rpc:multicall([Cp1_2, Cp2, Cp3], application, load, [app3()]), ?UNTIL(is_loaded(app3, [Cp1_2, Cp2, Cp3])), @@ -282,17 +274,17 @@ failover_comp(Conf) when is_list(Conf) -> false = is_started(app3, Cp3), ok = get_start_type(#st{normal = 3}), - % Stop cp2 and make sure cp1 starts app3 + %% Stop cp2 and make sure cp1 starts app3 stop_node_nice(Cp2), ?UNTIL(is_started(app3, Cp1_2)), ok = get_start_type(#st{normal = 3}), - % Stop cp1 and make sure cp3 starts app3 + %% Stop cp1 and make sure cp3 starts app3 stop_node_nice(Cp1_2), ?UNTIL(is_started(app3, Cp3)), ok = get_start_type(#st{normal = 6}), - % Restart cp2 and make sure it restarts app3 + %% Restart cp2 and make sure it restarts app3 {ok, Cp2_2} = start_node_config(Ncp2, NoSyncTime, Conf), global:sync(), ok = rpc:call(Cp2_2, application, load, [app3()]), @@ -302,17 +294,17 @@ failover_comp(Conf) when is_list(Conf) -> ?UNTIL(not is_started(app3, Cp3)), ok = get_start_type(#st{takeover = 3}), - % Restart cp1 and make sure it doesn't restart app3 + %% Restart cp1 and make sure it doesn't restart app3 {ok, Cp1_3} = start_node_config(Ncp1, NoSyncTime, Conf), global:sync(), ok = rpc:call(Cp1_3, application, load, [app3()]), true = is_loaded(app3, Cp1_3), ok = rpc:call(Cp1_3, application, start, [app3, permanent]), - test_server:sleep(5000), + ct:sleep(5000), false = is_started(app3, Cp1_3), true = is_started(app3, Cp2_2), - % Force takeover to cp1 + %% Force takeover to cp1 ok = rpc:call(Cp1_3, application, takeover, [app3, permanent]), ?UNTIL(is_started(app3, Cp1_3)), ?UNTIL(not is_started(app3, Cp2_2)), @@ -334,23 +326,21 @@ failover_comp(Conf) when is_list(Conf) -> %% Should be started in a CC view with: %% erl -sname XXX -rsh ctrsh where XX not in [cp1, cp2, cp3] %%----------------------------------------------------------------- -permissions(suite) -> []; -permissions(doc) -> - ["Tests permissions for distributed applications."]; +%% Tests permissions for distributed applications. permissions(Conf) when is_list(Conf) -> NodeNames = [Ncp1, Ncp2, Ncp3] = node_names([cp1, cp2, cp3], Conf), NoSyncTime = config_fun_fast(config2(NodeNames)), WithSyncTime = config_fun(config2(NodeNames)), - % Test [cp1, cp2, cp3] + %% Test [cp1, cp2, cp3] {ok, Cp1} = start_node_config(Ncp1, NoSyncTime, Conf), {ok, Cp2} = start_node_config(Ncp2, NoSyncTime, Conf), {ok, Cp3} = start_node_config(Ncp3, WithSyncTime, Conf), Cps = [Cp1, Cp2, Cp3], wait_for_ready_net(), - % Start app1 and make sure cp1 starts it + %% Start app1 and make sure cp1 starts it {[ok,ok,ok],[]} = rpc:multicall(Cps, application, load, [app1()]), ?UNTIL(is_loaded(app1, Cps)), @@ -359,50 +349,50 @@ permissions(Conf) when is_list(Conf) -> ?UNTIL(is_started(app1, Cp1)), false = is_started(app1, Cp2), - % Unpermit app1 on cp1, make sure cp2 starts it + %% Unpermit app1 on cp1, make sure cp2 starts it ok = rpc:call(Cp1, application, permit, [app1, false]), false = is_started(app1, Cp1), true = is_started(app1, Cp2), - % Unpermit app1 on cp2, make sure cp3 starts it + %% Unpermit app1 on cp2, make sure cp3 starts it ok = rpc:call(Cp2, application, permit, [app1, false]), false = is_started(app1, Cp1), false = is_started(app1, Cp2), true = is_started(app1, Cp3), - % Permit cp2 again + %% Permit cp2 again ok = rpc:call(Cp2, application, permit, [app1, true]), false = is_started(app1, Cp1), false = is_started(app1, Cp3), true = is_started(app1, Cp2), - % Start app3, make sure noone starts it + %% Start app3, make sure noone starts it {[ok,ok,ok],[]} = rpc:multicall(Cps, application, load, [app3()]), ?UNTIL(is_loaded(app3, Cps)), {[ok,ok,ok],[]} = rpc:multicall(Cps, application, start, [app3, permanent]), - test_server:sleep(1000), + ct:sleep(1000), false = is_started(app3, Cp1), false = is_started(app3, Cp2), false = is_started(app3, Cp3), - % Permit app3 on Cp3 + %% Permit app3 on Cp3 ok = rpc:call(Cp3, application, permit, [app3, true]), true = is_started(app3, Cp3), - % Permit app3 on Cp2, make sure it starts it + %% Permit app3 on Cp2, make sure it starts it ok = rpc:call(Cp2, application, permit, [app3, true]), true = is_started(app3, Cp2), false = is_started(app3, Cp3), - % Permit app3 on Cp1, make sure it doesn't start it + %% Permit app3 on Cp1, make sure it doesn't start it ok = rpc:call(Cp1, application, permit, [app3, true]), false = is_started(app3, Cp1), true = is_started(app3, Cp2), false = is_started(app3, Cp3), - % Stop Cp2, make sure Cp1 starts app3 + %% Stop Cp2, make sure Cp1 starts app3 stop_node_nice(Cp2), ?UNTIL(is_started(app3, Cp1)), @@ -414,15 +404,13 @@ permissions(Conf) when is_list(Conf) -> %% Should be started in a CC view with: %% erl -sname XXX -rsh ctrsh where XX not in [cp1, cp2, cp3] %%----------------------------------------------------------------- -load(suite) -> []; -load(doc) -> - ["Tests loading of distributed applications."]; +%% Tests loading of distributed applications. load(Conf) when is_list(Conf) -> NodeNames = [Ncp1, Ncp2, Ncp3] = node_names([cp1, cp2, cp3], Conf), NoSyncTime = config_fun_fast(config3(NodeNames)), WithSyncTime = config_fun(config3(NodeNames)), - % Test [cp1, cp2, cp3] + %% Test [cp1, cp2, cp3] {ok, Cp1} = start_node_config(Ncp1, NoSyncTime, Conf), {ok, Cp2} = start_node_config(Ncp2, NoSyncTime, Conf), {ok, Cp3} = start_node_config(Ncp3, WithSyncTime, Conf), @@ -438,7 +426,7 @@ load(Conf) when is_list(Conf) -> false = is_started(app1, Cp2), false = is_started(app1, Cp3), - % Load app1 with different specs and make sure we get an error + %% Load app1 with different specs and make sure we get an error {[{error,_},{error,_}],[]} = rpc:multicall([Cp1, Cp2], application, load, [app1(), d1(NodeNames)]), {error, _} = rpc:call(Cp3, application, load, [app1(), d2(NodeNames)]), @@ -451,15 +439,13 @@ load(Conf) when is_list(Conf) -> %%----------------------------------------------------------------- %% Same test as load/1, only with code path cache enabled. %%----------------------------------------------------------------- -load_use_cache(suite) -> []; -load_use_cache(doc) -> - ["Tests loading of distributed applications. Code path cache enabled."]; +%% Tests loading of distributed applications. Code path cache enabled. load_use_cache(Conf) when is_list(Conf) -> NodeNames = [Ncp1, Ncp2, Ncp3] = node_names([cp1, cp2, cp3], Conf), NoSyncTime = config_fun_fast(config3(NodeNames)), WithSyncTime = config_fun(config3(NodeNames)), - % Test [cp1, cp2, cp3] + %% Test [cp1, cp2, cp3] {ok, Cp1} = start_node_with_cache(Ncp1, NoSyncTime, Conf), {ok, Cp2} = start_node_with_cache(Ncp2, NoSyncTime, Conf), {ok, Cp3} = start_node_with_cache(Ncp3, WithSyncTime, Conf), @@ -474,7 +460,7 @@ load_use_cache(Conf) when is_list(Conf) -> ?UNTIL(is_started(app1, Cp1)), false = is_started(app1, Cp2), - % Load app1 with different specs and make sure we get an error + %% Load app1 with different specs and make sure we get an error {[{error,_},{error,_}],[]} = rpc:multicall([Cp1, Cp2], application, load, [app1(), d1(NodeNames)]), {error, _} = rpc:call(Cp3, application, load, [app1(), d2(NodeNames)]), @@ -488,9 +474,7 @@ load_use_cache(Conf) when is_list(Conf) -> %% Should be started in a CC view with: %% erl -sname XXX -rsh ctrsh where XX not in [cp1, cp2, cp3] %%----------------------------------------------------------------- -start_phases(suite) -> []; -start_phases(doc) -> - ["Tests new start phases and failover."]; +%% Tests new start phases and failover. start_phases(Conf) when is_list(Conf) -> %% start a help process to check the start type SpPid = spawn_link(?MODULE, start_phase, []), @@ -552,17 +536,15 @@ start_phases(Conf) when is_list(Conf) -> ok. -script_start(doc) -> - ["Start distributed applications from within a boot script. Test ", - "same as failover."]; -script_start(suite) -> []; +%% Start distributed applications from within a boot script. Test +%% same as failover. script_start(Conf) when is_list(Conf) -> %% start a help process to check the start type StPid = spawn_link(?MODULE, start_type, []), yes = global:register_name(st_type, StPid), - % Create the .app files and the boot script + %% Create the .app files and the boot script ok = create_app(), {{KernelVer,StdlibVer}, _} = create_script("latest"), case is_real_system(KernelVer, StdlibVer) of @@ -577,7 +559,7 @@ script_start(Conf) when is_list(Conf) -> NoSyncTime = config_fun_fast(config_fo(NodeNames)), WithSyncTime = config_fun(config_fo(NodeNames)), - % Test [cp1, cp2, cp3] + %% Test [cp1, cp2, cp3] {ok, Cp1} = start_node_boot_config(Ncp1, NoSyncTime, Conf, latest), {ok, Cp2} = start_node_boot_config(Ncp2, NoSyncTime, Conf, latest), {ok, Cp3} = start_node_boot_config(Ncp3, WithSyncTime, Conf, latest), @@ -589,16 +571,16 @@ script_start(Conf) when is_list(Conf) -> false = is_started(app1, Cp2), ok = get_start_type(#st{normal = 9}), - % Stop cp1 and make sure cp2 starts app1, app2 normally (no - % start_phases defined) and app_sp as failover (start_phases - % defined) + %% Stop cp1 and make sure cp2 starts app1, app2 normally (no + %% start_phases defined) and app_sp as failover (start_phases + %% defined) stop_node_nice(Cp1), ?UNTIL(is_started(app1, Cp2)), ?UNTIL(is_started(app2, Cp2)), ?UNTIL(is_started(app_sp, Cp2)), ok = get_start_type(#st{normal = 6, failover = 3}), - % Restart cp1, Cp1 takesover app1 and app2 + %% Restart cp1, Cp1 takesover app1 and app2 {ok, Cp1_2} = start_node_boot_config(Ncp1, NoSyncTime, Conf, latest), global:sync(), ?UNTIL(is_started(app1, Cp1_2)), @@ -609,20 +591,20 @@ script_start(Conf) when is_list(Conf) -> ?UNTIL(not is_started(app2, Cp2)), ok = get_start_type(#st{takeover = 6}), - % Stop cp2 and make sure cp1 starts app_sp. + %% Stop cp2 and make sure cp1 starts app_sp. false = is_started(app_sp, Cp1_2), stop_node_nice(Cp2), ?UNTIL(is_started(app_sp, Cp1_2)), ok = get_start_type(#st{failover = 3}), - % Stop cp1 and make sure cp3 starts app1, app2 and app_sp + %% Stop cp1 and make sure cp3 starts app1, app2 and app_sp stop_node_nice(Cp1_2), ?UNTIL(is_started(app_sp, Cp3)), ?UNTIL(is_started(app1, Cp3)), ?UNTIL(is_started(app2, Cp3)), ok = get_start_type(#st{normal = 6, failover = 3}), - % Restart cp2 and make sure it takesover app1, app2 and app_sp + %% Restart cp2 and make sure it takesover app1, app2 and app_sp {ok, Cp2_2} = start_node_boot_config(Ncp2, NoSyncTime, Conf, latest), global:sync(), ?UNTIL(is_started(app_sp, Cp2_2)), @@ -633,7 +615,7 @@ script_start(Conf) when is_list(Conf) -> ?UNTIL(not is_started(app2, Cp3)), ok = get_start_type(#st{takeover = 9}), - % Restart cp1 and make sure it takesover app1, app2 + %% Restart cp1 and make sure it takesover app1, app2 {ok, Cp1_3} = start_node_boot_config(Ncp1, NoSyncTime, Conf, latest), global:sync(), ?UNTIL(is_started(app1, Cp1_3)), @@ -644,7 +626,7 @@ script_start(Conf) when is_list(Conf) -> ?UNTIL(not is_started(app2, Cp2_2)), ok = get_start_type(#st{takeover = 6}), - % Force takeover to cp1 + %% Force takeover to cp1 ok = rpc:call(Cp1_3, application, takeover, [app_sp, permanent]), ?UNTIL(is_started(app_sp, Cp1_3)), ?UNTIL(not is_started(app_sp, Cp2_2)), @@ -667,15 +649,13 @@ script_start(Conf) when is_list(Conf) -> ok. -permit_false_start_local(doc) -> - ["Start local applications with permission false. Set", - "permit true on different nodes."]; -permit_false_start_local(suite) -> []; +%% Start local applications with permission false. Set +%% permit true on different nodes. permit_false_start_local(Conf) when is_list(Conf) -> %% This configuration does not start dist_ac. Config = write_config_file(fun config_perm/1, Conf), - % Test [cp1, cp2, cp3] + %% Test [cp1, cp2, cp3] [Ncp1, Ncp2, Ncp3] = node_names([cp1, cp2, cp3], Conf), {ok, Cp1} = start_node(Ncp1, Config), {ok, Cp2} = start_node(Ncp2, Config), @@ -693,99 +673,99 @@ permit_false_start_local(Conf) when is_list(Conf) -> {[ok,ok,ok],[]} = rpc:multicall([Cp1, Cp2, Cp3], application, load, [app3()]), - test_server:sleep(1000), + ct:sleep(1000), false = is_started(app1, Cp1), false = is_started(app1, Cp2), false = is_started(app1, Cp3), - %Permit a not started application + %% Permit a not started application ok = rpc:call(Cp1, application, permit, [app3, true]), - test_server:sleep(1000), + ct:sleep(1000), false = is_started(app3, Cp1), false = is_started(app3, Cp2), false = is_started(app3, Cp3), - %Permit a not loaded application + %% Permit a not loaded application {error,{not_loaded,app_notloaded}} = rpc:call(Cp1, application, permit, [app_notloaded, true]), - test_server:sleep(1000), + ct:sleep(1000), false = is_started(app_notloaded, Cp1), false = is_started(app_notloaded, Cp2), false = is_started(app_notloaded, Cp3), - %Unpermit a not started application + %% Unpermit a not started application ok = rpc:call(Cp1, application, permit, [app3, false]), - test_server:sleep(1000), + ct:sleep(1000), false = is_started(app3, Cp1), false = is_started(app3, Cp2), false = is_started(app3, Cp3), - %Unpermit a not loaded application + %% Unpermit a not loaded application {error,{not_loaded,app_notloaded}} = rpc:call(Cp1, application, permit, [app_notloaded, false]), - test_server:sleep(1000), + ct:sleep(1000), false = is_started(app_notloaded, Cp1), false = is_started(app_notloaded, Cp2), false = is_started(app_notloaded, Cp3), - % Permit app1 on CP1 and make sure it is started + %% Permit app1 on CP1 and make sure it is started ok = rpc:call(Cp1, application, permit, [app1, true]), ?UNTIL(is_started(app1, Cp1)), false = is_started(app1, Cp2), false = is_started(app1, Cp3), - % Permit it again + %% Permit it again ok = rpc:call(Cp1, application, permit, [app1, true]), - test_server:sleep(1000), + ct:sleep(1000), true = is_started(app1, Cp1), false = is_started(app1, Cp2), false = is_started(app1, Cp3), - % Permit app2 on CP1 and make sure it is started + %% Permit app2 on CP1 and make sure it is started ok = rpc:call(Cp1, application, permit, [app2, true]), ?UNTIL(is_started(app2, Cp1)), false = is_started(app2, Cp2), false = is_started(app2, Cp3), - % Permit app1 on CP2 and make sure it is started + %% Permit app1 on CP2 and make sure it is started ok = rpc:call(Cp2, application, permit, [app1, true]), ?UNTIL(is_started(app1, Cp2)), true = is_started(app1, Cp1), false = is_started(app1, Cp3), - % Unpermit app1 on CP1 and make sure it is stopped + %% Unpermit app1 on CP1 and make sure it is stopped ok = rpc:call(Cp1, application, permit, [app1, false]), ?UNTIL(false =:= is_started(app1, Cp1)), true = is_started(app1, Cp2), false = is_started(app1, Cp3), - % Unpermit it agin + %% Unpermit it agin ok = rpc:call(Cp1, application, permit, [app1, false]), - test_server:sleep(1000), + ct:sleep(1000), false = is_started(app1, Cp1), true = is_started(app1, Cp2), false = is_started(app1, Cp3), - % Permit app1 on CP1 and make sure it is started + %% Permit app1 on CP1 and make sure it is started ok = rpc:call(Cp1, application, permit, [app1, true]), ?UNTIL(is_started(app1, Cp1)), true = is_started(app1, Cp2), false = is_started(app1, Cp3), - % Unpermit app1 on CP1 and make sure it is stopped + %% Unpermit app1 on CP1 and make sure it is stopped ok = rpc:call(Cp1, application, permit, [app1, false]), ?UNTIL(false =:= is_started(app1, Cp1)), true = is_started(app1, Cp2), false = is_started(app1, Cp3), - % Unpermit app1 on CP2 and make sure it is stopped + %% Unpermit app1 on CP2 and make sure it is stopped ok = rpc:call(Cp2, application, permit, [app1, false]), - test_server:sleep(1000), + ct:sleep(1000), ?UNTIL(false =:= is_started(app1, Cp2)), false = is_started(app1, Cp1), false = is_started(app1, Cp3), - % Unpermit app2 on CP1 and make sure it is stopped + %% Unpermit app2 on CP1 and make sure it is stopped ok = rpc:call(Cp1, application, permit, [app2, false]), ?UNTIL(false =:= is_started(app2, Cp2)), false = is_started(app2, Cp1), @@ -797,16 +777,14 @@ permit_false_start_local(Conf) when is_list(Conf) -> ok. -permit_false_start_dist(doc) -> - ["Start distributed applications with permission false. Set", - "permit true on different nodes."]; -permit_false_start_dist(suite) -> []; +%% Start distributed applications with permission false. Set +%% permit true on different nodes. permit_false_start_dist(Conf) when is_list(Conf) -> NodeNames = [Ncp1, Ncp2, Ncp3] = node_names([cp1, cp2, cp3], Conf), NoSyncTime = config_fun_fast(config_perm2(NodeNames)), WithSyncTime = config_fun(config_perm2(NodeNames)), - % Test [cp1, cp2, cp3] + %% Test [cp1, cp2, cp3] {ok, Cp1} = start_node_config(Ncp1, NoSyncTime, Conf), {ok, Cp2} = start_node_config(Ncp2, NoSyncTime, Conf), {ok, Cp3} = start_node_config(Ncp3, WithSyncTime, Conf), @@ -821,36 +799,36 @@ permit_false_start_dist(Conf) when is_list(Conf) -> {[ok,ok,ok],[]} = rpc:multicall(Cps, application, load, [app2()]), - test_server:sleep(1000), + ct:sleep(1000), false = is_started(app1, Cp1), false = is_started(app1, Cp2), false = is_started(app1, Cp3), - %Permit a not started application + %% Permit a not started application ok = rpc:call(Cp1, application, permit, [app2, true]), - test_server:sleep(1000), + ct:sleep(1000), false = is_started(app2, Cp1), false = is_started(app2, Cp2), false = is_started(app2, Cp3), - %Permit a not loaded application + %% Permit a not loaded application {error,{not_loaded,app3}} = rpc:call(Cp1, application, permit, [app3, true]), - test_server:sleep(1000), + ct:sleep(1000), false = is_started(app3, Cp1), false = is_started(app3, Cp2), false = is_started(app3, Cp3), - %Unpermit a not started application + %% Unpermit a not started application ok = rpc:call(Cp1, application, permit, [app2, false]), {[ok,ok,ok],[]} = rpc:multicall([Cp1, Cp2, Cp3], application, start, [app2, permanent]), - test_server:sleep(1000), + ct:sleep(1000), false = is_started(app2, Cp1), false = is_started(app2, Cp2), false = is_started(app2, Cp3), - %Unpermit a not loaded application + %% Unpermit a not loaded application {error,{not_loaded,app3}} = rpc:call(Cp1, application, permit, [app3, false]), {[ok,ok,ok],[]} = @@ -858,42 +836,42 @@ permit_false_start_dist(Conf) when is_list(Conf) -> ?UNTIL(is_loaded(app3, Cps)), {[ok,ok,ok],[]} = rpc:multicall(Cps, application, start, [app3, permanent]), - test_server:sleep(1000), + ct:sleep(1000), false = is_started(app3, Cp1), false = is_started(app3, Cp2), false = is_started(app3, Cp3), - % Permit app1 on CP1 and make sure it is started + %% Permit app1 on CP1 and make sure it is started ok = rpc:call(Cp1, application, permit, [app1, true]), ?UNTIL(is_started(app1, Cp1)), false = is_started(app1, Cp2), false = is_started(app1, Cp3), - % Permit it again + %% Permit it again ok = rpc:call(Cp1, application, permit, [app1, true]), ?UNTIL(is_started(app1, Cp1)), false = is_started(app1, Cp2), false = is_started(app1, Cp3), - % Permit app2 on CP1 and make sure it is started + %% Permit app2 on CP1 and make sure it is started ok = rpc:call(Cp1, application, permit, [app2, true]), ?UNTIL(is_started(app2, Cp1)), false = is_started(app2, Cp2), false = is_started(app2, Cp3), - % Permit app1 on CP2 and make sure it is not started + %% Permit app1 on CP2 and make sure it is not started ok = rpc:call(Cp2, application, permit, [app1, true]), - test_server:sleep(1000), + ct:sleep(1000), true = is_started(app1, Cp1), false = is_started(app1, Cp2), false = is_started(app1, Cp3), - % Crash CP1 and make sure app1, but not app2, is started on CP2 + %% Crash CP1 and make sure app1, but not app2, is started on CP2 stop_node_nice(Cp1), ?UNTIL(is_started(app1, Cp2)), false = is_started(app2, Cp2), - % Restart CP1 again, check nothing is running on it + %% Restart CP1 again, check nothing is running on it {ok, Cp1_2} = start_node_config(Ncp1, NoSyncTime, Conf), global:sync(), ok = rpc:call(Cp1_2, application, load, [app1()]), @@ -908,19 +886,19 @@ permit_false_start_dist(Conf) when is_list(Conf) -> false = is_started(app1, Cp1_2), false = is_started(app2, Cp1_2), - % Permit app3 on CP3 and make sure it is started + %% Permit app3 on CP3 and make sure it is started ok = rpc:call(Cp3, application, permit, [app3, true]), ?UNTIL(is_started(app3, Cp3)), false = is_started(app3, Cp1_2), false = is_started(app3, Cp2), - % Permit app3 on CP1 and make sure it is moved there from CP3 + %% Permit app3 on CP1 and make sure it is moved there from CP3 ok = rpc:call(Cp1_2, application, permit, [app3, true]), ?UNTIL(is_started(app3, Cp1_2)), false = is_started(app3, Cp2), false = is_started(app3, Cp3), - % Unpermit app3 on CP3 and CP1 and make sure it is stopped + %% Unpermit app3 on CP3 and CP1 and make sure it is stopped ok = rpc:call(Cp3, application, permit, [app3, false]), ok = rpc:call(Cp1_2, application, permit, [app3, false]), ?UNTIL(false =:= is_started(app3, Cp1_2)), @@ -932,27 +910,25 @@ permit_false_start_dist(Conf) when is_list(Conf) -> stop_node_nice(Cp3), ok. -nodedown_start(doc) -> - ["app1 distributed as [cp1, cp2]. Call application:start(app1) on", - "cp2, but not on cp1. Kill cp1. Make sure app1 is started on cp2."]; -nodedown_start(suite) -> []; +%% app1 distributed as [cp1, cp2]. Call application:start(app1) on +%% cp2, but not on cp1. Kill cp1. Make sure app1 is started on cp2. nodedown_start(Conf) when is_list(Conf) -> NodeNames = [Ncp1, Ncp2] = node_names([cp1, cp2], Conf), NoSyncTime = config_fun_fast(config4(NodeNames)), WithSyncTime = config_fun(config4(NodeNames)), - % Test [cp1, cp2] + %% Test [cp1, cp2] {ok, Cp1} = start_node_config(Ncp1, NoSyncTime, Conf), {ok, Cp2} = start_node_config(Ncp2, WithSyncTime, Conf), wait_for_ready_net(), - % Start app1 and make sure cp1 starts it + %% Start app1 and make sure cp1 starts it {[ok,ok],[]} = rpc:multicall([Cp1, Cp2], application, load, [app1()]), _ = rpc:cast(Cp2, application, start, [app1, permanent]), - test_server:sleep(1000), + ct:sleep(1000), - % Crash CP1 and make sure app1 is started on CP2 + %% Crash CP1 and make sure app1 is started on CP2 stop_node_nice(Cp1), ?UNTIL(is_started(app1, Cp2)), @@ -960,8 +936,7 @@ nodedown_start(Conf) when is_list(Conf) -> ok. -ensure_started(suite) -> []; -ensure_started(doc) -> ["Test application:ensure_started/1."]; +%% Test application:ensure_started/1. ensure_started(_Conf) -> {ok, Fd} = file:open("app1.app", [write]), @@ -980,8 +955,7 @@ ensure_started(_Conf) -> ok = application:unload(app1), ok. -ensure_all_started(suite) -> []; -ensure_all_started(doc) -> ["Test application:ensure_all_started/1-2."]; +%% Test application:ensure_all_started/1-2. ensure_all_started(_Conf) -> {ok, Fd1} = file:open("app1.app", [write]), @@ -1068,11 +1042,9 @@ ensure_all_started(_Conf) -> %% Ticket: OTP-1586 %% Slogan: recursive load of applications fails %%----------------------------------------------------------------- -otp_1586(suite) -> []; -otp_1586(doc) -> - ["Test recursive load of applications."]; +%% Test recursive load of applications. otp_1586(Conf) when is_list(Conf) -> - Dir = ?config(priv_dir,Conf), + Dir = proplists:get_value(priv_dir,Conf), {ok, Fd} = file:open(filename:join(Dir, "app5.app"), [write]), w_app5(Fd), file:close(Fd), @@ -1089,21 +1061,19 @@ otp_1586(Conf) when is_list(Conf) -> %% Slogan: start of distrib apps fails when the nodes start %% simultaneously %%----------------------------------------------------------------- -otp_2078(suite) -> []; -otp_2078(doc) -> - ["Test start of distrib apps fails when the nodes start simultaneously."]; +%% Test start of distrib apps fails when the nodes start simultaneously. otp_2078(Conf) when is_list(Conf) -> NodeNames = [Ncp1, Ncp2] = node_names([cp1, cp2], Conf), NoSyncTime = config_fun_fast(config4(NodeNames)), WithSyncTime = config_fun(config4(NodeNames)), - % Test [cp1, cp2] + %% Test [cp1, cp2] {ok, Cp1} = start_node_config(Ncp1, NoSyncTime, Conf), {ok, Cp2} = start_node_config(Ncp2, WithSyncTime, Conf), Cps = [Cp1, Cp2], wait_for_ready_net(), - % Start app1 and make sure cp1 starts it + %% Start app1 and make sure cp1 starts it {[ok,ok],[]} = rpc:multicall(Cps, application, load, [app1()]), ?UNTIL(is_loaded(app1, Cps)), @@ -1111,8 +1081,8 @@ otp_2078(Conf) when is_list(Conf) -> ?UNTIL(is_started(app1, Cp1)), false = is_started(app1, Cp2), - % Start app1 on cp2; make sure it works (the bug was that this start - % returned error) + %% Start app1 on cp2; make sure it works (the bug was that this start + %% returned error) ok = rpc:call(Cp2, application, start, [app1, permanent]), true = is_started(app1, Cp1), false = is_started(app1, Cp2), @@ -1121,15 +1091,13 @@ otp_2078(Conf) when is_list(Conf) -> stop_node_nice(Cp2), ok. -otp_2012(suite) -> []; -otp_2012(doc) -> - ["Test change of configuration parameters without changing code."]; +%% Test change of configuration parameters without changing code. otp_2012(Conf) when is_list(Conf) -> %% start a help process to check the config change CcPid = spawn_link(?MODULE, conf_change, []), yes = global:register_name(conf_change, CcPid), - % Write a .app file + %% Write a .app file {ok, Fd} = file:open("app1.app", [write]), w_app1(Fd), file:close(Fd), @@ -1137,7 +1105,7 @@ otp_2012(Conf) when is_list(Conf) -> w_app1(Fd2), file:close(Fd2), - % Start app1 + %% Start app1 ok = application:load(app1()), ok = application:start(app1, permanent), @@ -1148,7 +1116,7 @@ otp_2012(Conf) when is_list(Conf) -> ok = application_controller:config_change(EnvBefore), ok = get_conf_change([{[], [{new1, hi}, {new2, moi}], []}]), - % Start app2 + %% Start app2 ok = application:load(app2()), ok = application:start(app2, permanent), @@ -1172,11 +1140,9 @@ otp_2012(Conf) when is_list(Conf) -> %% Ticket: OTP-2718 %% Slogan: transient app which fails during start is ignored %%----------------------------------------------------------------- -otp_2718(suite) -> []; -otp_2718(doc) -> - ["Test fail of transient app at start."]; +%% Test fail of transient app at start. otp_2718(Conf) when is_list(Conf) -> - {ok, Cp1} = start_node_args(cp1, "-pa " ++ ?config(data_dir,Conf)), + {ok, Cp1} = start_node_args(cp1, "-pa " ++ proplists:get_value(data_dir,Conf)), wait_for_ready_net(), %% normal exit from the application @@ -1184,7 +1150,7 @@ otp_2718(Conf) when is_list(Conf) -> ?UNTIL(is_loaded(trans_normal, Cp1)), {error, {{'EXIT',normal},_}} = rpc:call(Cp1, application, start, [trans_normal, transient]), - test_server:sleep(2000), + ct:sleep(2000), false = is_started(trans_normal, Cp1), %% abnormal exit from the application @@ -1192,7 +1158,7 @@ otp_2718(Conf) when is_list(Conf) -> {error, {bad_return,{{trans_abnormal_sup,start,[normal,[]]}, {'EXIT',abnormal}}}} = rpc:call(Cp1, application, start, [trans_abnormal, transient]), - test_server:sleep(3000), + ct:sleep(3000), {badrpc,nodedown} = which_applications(Cp1), ok. @@ -1200,11 +1166,9 @@ otp_2718(Conf) when is_list(Conf) -> %% Ticket: OTP-2973 %% Slogan: application:start does not test if an appl is already starting... %%----------------------------------------------------------------- -otp_2973(suite) -> []; -otp_2973(doc) -> - ["Test of two processes simultanously starting the same application."]; +%% Test of two processes simultanously starting the same application. otp_2973(Conf) when is_list(Conf) -> - % Write a .app file + %% Write a .app file {ok, Fd} = file:open("app0.app", [write]), w_app(Fd, app0()), file:close(Fd), @@ -1221,14 +1185,14 @@ otp_2973(Conf) when is_list(Conf) -> {Pid2, res, Res2x} -> {Res1x, Res2x} after 2000 -> - test_server:fail(timeout_pid2) + ct:fail(timeout_pid2) end; {Pid2, res, Res2x} -> receive {Pid1, res, Res1x} -> {Res1x, Res2x} after 2000 -> - test_server:fail(timeout_pid1) + ct:fail(timeout_pid1) end end, @@ -1242,11 +1206,11 @@ otp_2973(Conf) when is_list(Conf) -> _ -> Txt = io_lib:format("Illegal results from start: ~p ~p ", [Res1, Res2]), - test_server:fail(lists:flatten(Txt)) + ct:fail(lists:flatten(Txt)) end, - % Write a .app file + %% Write a .app file {ok, Fda} = file:open("app_start_error.app", [write]), w_app_start_error(Fda), file:close(Fda), @@ -1260,14 +1224,14 @@ otp_2973(Conf) when is_list(Conf) -> {Pid2, res, Res2y} -> {Res1y, Res2y} after 2000 -> - test_server:fail(timeout_pid2) + ct:fail(timeout_pid2) end; {Pid2, res, Res2y} -> receive {Pid1, res, Res1y} -> {Res1y, Res2y} after 2000 -> - test_server:fail(timeout_pid1) + ct:fail(timeout_pid1) end end, @@ -1277,7 +1241,7 @@ otp_2973(Conf) when is_list(Conf) -> ok; _ -> Txta = io_lib:format("Illegal results from start ~p ~p ",[Res1a, Res2a]), - test_server:fail(lists:flatten(Txta)) + ct:fail(lists:flatten(Txta)) end, ok. @@ -1288,36 +1252,34 @@ otp_2973(Conf) when is_list(Conf) -> %% Ticket: OTP-3184 %% Slogan: crash the node if permanent appl has illegal env parameter values %%----------------------------------------------------------------- -otp_3184(suite) -> []; -otp_3184(doc) -> - ["When a distributed application is started the permit flag is checked " - "that the permit flag is not changed during the start. " - "Te check must only be made if the application is started on the own node"]; +%% When a distributed application is started the permit flag is checked +%% that the permit flag is not changed during the start. +%% The check must only be made if the application is started on the own node. otp_3184(Conf) when is_list(Conf) -> NodeNames = [Ncp1, Ncp2] = node_names([cp1, cp2], Conf), NoSyncTime = config_fun_fast(config3184(NodeNames)), WithSyncTime = config_fun(config3184(NodeNames)), - % Test [cp1, cp2] + %% Test [cp1, cp2] {ok, Cp1} = start_node_config(Ncp1, NoSyncTime, Conf), {ok, Cp2} = start_node_config(Ncp2, WithSyncTime, Conf), wait_for_ready_net(), - % Start app1 and make sure it is not started + %% Start app1 and make sure it is not started {[ok,ok],[]} = rpc:multicall([Cp1, Cp2], application, load, [app1()]), - test_server:sleep(3000), + ct:sleep(3000), false = is_started(app1, Cp1), false = is_started(app1, Cp2), - % Start app1 on cp1 + %% Start app1 on cp1 ok = rpc:call(Cp1, application, permit, [app1, true]), ok = rpc:call(Cp1, application, start, [app1, permanent]), ok = rpc:call(Cp2, application, start, [app1, permanent]), ?UNTIL(is_started(app1, Cp1)), false = is_started(app1, Cp2), - % Check that the application is marked as running in application_controller + %% Check that the application is marked as running in application_controller X = rpc:call(Cp1, application_controller, info, []), {value, {running, Xrunning}} = lists:keysearch(running, 1, X), {value, Xapp1} = lists:keysearch(app1, 1, Xrunning), @@ -1336,15 +1298,13 @@ otp_3184(Conf) when is_list(Conf) -> %% Ticket: OTP-3002 %% Slogan: crash the node if permanent appl has illegal env parameter values %%----------------------------------------------------------------- -otp_3002(suite) -> []; -otp_3002(doc) -> - ["crash the node if permanent appl has illegal env parameter values."]; +%% crash the node if permanent appl has illegal env parameter values. otp_3002(Conf) when is_list(Conf) -> - % Create the boot script + %% Create the boot script {{KernelVer,StdlibVer}, {LatestDir, LatestName}} = create_script_3002("script_3002"), - ?t:format(0, "LatestDir = ~p~n", [LatestDir]), - ?t:format(0, "LatestName = ~p~n", [LatestName]), + ct:pal(?HI_VERBOSITY, "LatestDir = ~p~n", [LatestDir]), + ct:pal(?HI_VERBOSITY, "LatestName = ~p~n", [LatestName]), case is_real_system(KernelVer, StdlibVer) of true -> @@ -1370,10 +1330,9 @@ otp_3002(Conf) when is_list(Conf) -> %% when it received dist_ac_app_stopped). %%----------------------------------------------------------------- -otp_4066(suite) -> []; -otp_4066(doc) -> ["Check that application stop don't cause dist_ac crash"]; +%% Check that application stop don't cause dist_ac crash. otp_4066(Conf) when is_list(Conf) -> - % Write config files + %% Write config files [Ncp1, Ncp2] = node_names([cp1, cp2], Conf), Host = from($@, atom_to_list(node())), Cp1 = list_to_atom(Ncp1 ++ "@" ++ Host), @@ -1381,12 +1340,12 @@ otp_4066(Conf) when is_list(Conf) -> AllNodes = [Cp1, Cp2], App1Nodes = {app1, AllNodes}, - Dir = ?config(priv_dir,Conf), + Dir = proplists:get_value(priv_dir,Conf), {ok, FdC} = file:open(filename:join(Dir, "otp_4066.config"), [write]), write_config(FdC, config_4066(AllNodes, 5000, [App1Nodes])), file:close(FdC), - % Write the app1.app file + %% Write the app1.app file {ok, FdA12} = file:open(filename:join(Dir, "app1.app"), [write]), w_app1(FdA12), file:close(FdA12), @@ -1401,29 +1360,29 @@ otp_4066(Conf) when is_list(Conf) -> ok = rpc:call(Cp1, application, start, [app1]), wait_until_started(app1, [Cp1]), - test_server:format("--- App1 started at Cp1 ---~n", []), + io:format("--- App1 started at Cp1 ---~n", []), print_dac_state(AllNodes), - % Cp2 previously crashed on this stop + %% Cp2 previously crashed on this stop ok = rpc:call(Cp1, application, stop, [app1]), wait_until_stopped(app1, [Cp1]), - test_server:format("--- App1 stopped at Cp1 ---~n", []), + io:format("--- App1 stopped at Cp1 ---~n", []), print_dac_state(AllNodes), ok = rpc:call(Cp1, application, start, [app1]), wait_until_started(app1, [Cp1]), - test_server:format("--- App1 started at Cp1 ---~n", []), + io:format("--- App1 started at Cp1 ---~n", []), print_dac_state(AllNodes), ok = rpc:call(Cp2, application, load, [app1, App1Nodes]), ok = rpc:call(Cp2, application, start, [app1]), - test_server:format("--- App1 started at Cp2 ---~n", []), + io:format("--- App1 started at Cp2 ---~n", []), print_dac_state(AllNodes), stop_node_nice(Cp1), wait_until_started(app1, [Cp2]), - test_server:format("--- Cp1 crashed; failover to Cp2 ---~n", []), + io:format("--- Cp1 crashed; failover to Cp2 ---~n", []), print_dac_state(Cp2), stop_node_nice(Cp2), @@ -1439,7 +1398,7 @@ write_config(Fd, Config) -> print_dac_state(Node) when is_atom(Node) -> State = gen_server:call({dist_ac, Node}, info), - test_server:format(" * dist_ac state on node ~p:~n ~p~n", + io:format(" * dist_ac state on node ~p:~n ~p~n", [Node, State]); print_dac_state(Nodes) when is_list(Nodes) -> lists:foreach(fun (N) -> print_dac_state(N) end, Nodes). @@ -1449,9 +1408,7 @@ print_dac_state(Nodes) when is_list(Nodes) -> %% Ticket: OTP-4227 %% Slogan: Bad return value from application. %%----------------------------------------------------------------- -otp_4227(suite) -> []; -otp_4227(doc) -> - ["Test start of depending app when required app crashed."]; +%% Test start of depending app when required app crashed. otp_4227(Conf) when is_list(Conf) -> NodeNames = [Ncp1, Ncp2] = node_names([cp1, cp2], Conf), NoSyncTime = config_fun_fast(config_4227(NodeNames)), @@ -1474,11 +1431,11 @@ otp_4227(Conf) when is_list(Conf) -> %% Start app9 and brutally kill it, then try to start app10 ok = rpc:call(Cp1, application, start, [app9]), - test_server:sleep(1000), + ct:sleep(1000), Pid9 = rpc:call(Cp1, erlang, whereis, [ch_sup19]), true = erlang:is_pid(Pid9), true = erlang:exit(Pid9, kill), - test_server:sleep(1000), + ct:sleep(1000), %% This gave {error, no_report} before the patch {error, {not_running, app9}} = @@ -1517,7 +1474,7 @@ otp_5363(Conf) when is_list(Conf) -> %% the code, but only that the correct processes ARE killed. OldPath = code:get_path(), - code:add_patha(?config(data_dir,Conf)), + code:add_patha(proplists:get_value(data_dir,Conf)), try ok = application:load(app_group_leader()), ok = application:start(group_leader), @@ -1531,7 +1488,7 @@ otp_5363(Conf) when is_list(Conf) -> undefined = whereis(nisse); Bad -> io:format("~p\n", [Bad]), - ?t:fail() + ct:fail(failed) end after code:set_path(OldPath) @@ -1542,14 +1499,12 @@ otp_5363(Conf) when is_list(Conf) -> %% Ticket: OTP-5606 %% Slogan: Problems with starting a distributed application %%----------------------------------------------------------------- -otp_5606(suite) -> []; -otp_5606(doc) -> - ["Test of several processes simultanously starting the same " - "distributed application."]; +%% Test of several processes simultaneously starting the same +%% distributed application. otp_5606(Conf) when is_list(Conf) -> %% Write a config file - Dir = ?config(priv_dir, Conf), + Dir = proplists:get_value(priv_dir, Conf), {ok, Fd} = file:open(filename:join(Dir, "sys.config"), [write]), NodeNames = [Ncp1, Ncp2] = node_names([cp1, cp2], Conf), (config4(NodeNames))(Fd, 10000), @@ -1585,7 +1540,7 @@ otp_5606(Conf) when is_list(Conf) -> [Res1, Res2, Res3, Res4] -> Txt = io_lib:format("Illegal results from start ~p ~p ~p ~p", [Res1, Res2, Res3, Res4]), - test_server:fail(lists:flatten(Txt)) + ct:fail(lists:flatten(Txt)) end, {error, {already_started, app1}} = @@ -1600,7 +1555,7 @@ otp_5606_loop(ResL) when length(ResL)<4 -> {_Pid, Res} -> otp_5606_loop([Res|ResL]) after 5000 -> - test_server:fail(timeout_waiting_for_res) + ct:fail(timeout_waiting_for_res) end; otp_5606_loop(ResL) -> ResL. @@ -1612,11 +1567,10 @@ loop5606(Pid) -> Pid ! {self(), Res} end. -get_env(suite) -> []; -get_env(doc) -> - ["Tests get_env/* functions"]; +%% Tests get_env/* functions. get_env(Conf) when is_list(Conf) -> - {ok, _} = application:get_env(kernel, error_logger), + ok = application:set_env(kernel, new_var, new_val), + {ok, new_val} = application:get_env(kernel, new_var), undefined = application:get_env(undefined_app, a), undefined = application:get_env(kernel, error_logger_xyz), default = application:get_env(kernel, error_logger_xyz, default), @@ -1626,14 +1580,12 @@ get_env(Conf) when is_list(Conf) -> %% Should be started in a CC view with: %% erl -sname XXX -rsh ctrsh where XX not in [cp1, cp2, cp3] %%----------------------------------------------------------------- -get_key(suite) -> []; -get_key(doc) -> - ["Tests read the .app keys."]; +%% Tests read the .app keys. get_key(Conf) when is_list(Conf) -> NodeNames = [Ncp1, _Ncp2, _Ncp3] = node_names([cp1, cp2, cp3], Conf), WithSyncTime = config_fun(config_inc(NodeNames)), - % Test [cp1, cp2, cp3] + %% Test [cp1, cp2, cp3] {ok, Cp1} = start_node_config(Ncp1, WithSyncTime, Conf), ok = rpc:call(Cp1, application, load, [appinc(), d3(NodeNames)]), @@ -1652,8 +1604,7 @@ get_key(Conf) when is_list(Conf) -> {ok, [{init, [kalle]}, {takeover, []}, {go, [sune]}]} = rpc:call(Cp1, application, get_key, [appinc, start_phases]), {ok, Env} = rpc:call(Cp1, application, get_key, [appinc ,env]), - [{included_applications,[appinc1,appinc2]}, - {own2,val2},{own_env1,value1}] = lists:sort(Env), + [{own2,val2},{own_env1,value1}] = lists:sort(Env), {ok, []} = rpc:call(Cp1, application, get_key, [appinc, modules]), {ok, {application_starter, [ch_sup, {appinc, 41, 43}] }} = rpc:call(Cp1, application, get_key, [appinc, mod]), @@ -1674,8 +1625,7 @@ get_key(Conf) when is_list(Conf) -> {mod, {application_starter, [ch_sup, {appinc, 41, 43}] }}, {start_phases, [{init, [kalle]}, {takeover, []}, {go, [sune]}]}]} = rpc:call(Cp1, application, get_all_key, [appinc]), - [{included_applications,[appinc1,appinc2]}, - {own2,val2},{own_env1,value1}] = lists:sort(Env), + [{own2,val2},{own_env1,value1}] = lists:sort(Env), {ok, "Test of new app file, including appnew"} = gen_server:call({global, {ch,41}}, {get_pid_key, description}), @@ -1692,8 +1642,7 @@ get_key(Conf) when is_list(Conf) -> {ok, [{init, [kalle]}, {takeover, []}, {go, [sune]}]} = gen_server:call({global, {ch,41}}, {get_pid_key, start_phases}), {ok, Env} = gen_server:call({global, {ch,41}}, {get_pid_key, env}), - [{included_applications,[appinc1,appinc2]}, - {own2,val2},{own_env1,value1}] = lists:sort(Env), + [{own2,val2},{own_env1,value1}] = lists:sort(Env), {ok, []} = gen_server:call({global, {ch,41}}, {get_pid_key, modules}), {ok, {application_starter, [ch_sup, {appinc, 41, 43}] }} = @@ -1720,8 +1669,7 @@ get_key(Conf) when is_list(Conf) -> {mod, {application_starter, [ch_sup, {appinc, 41, 43}] }}, {start_phases, [{init, [kalle]}, {takeover, []}, {go, [sune]}]}]} = gen_server:call({global, {ch,41}}, get_pid_all_key), - [{included_applications,[appinc1,appinc2]}, - {own2,val2},{own_env1,value1}] = lists:sort(Env), + [{own2,val2},{own_env1,value1}] = lists:sort(Env), stop_node_nice(Cp1), ok. @@ -1730,8 +1678,7 @@ get_key(Conf) when is_list(Conf) -> %%% Testing of change of distributed parameter. %%%----------------------------------------------------------------- -distr_changed_tc1(suite) -> []; -distr_changed_tc1(doc) -> ["Test change of distributed parameter."]; +%% Test change of distributed parameter. distr_changed_tc1(Conf) when is_list(Conf) -> {OldKernel, OldEnv, {Cp1, Cp2, Cp3}, {_Ncp1, _Ncp2, _Ncp3}, _Config2} = @@ -1756,7 +1703,7 @@ distr_changed_tc1(Conf) when is_list(Conf) -> rpc:multicall([Cp1, Cp2, Cp3], application_controller, config_change, [OldEnv]), - test_server:sleep(7000), + ct:sleep(7000), DcInfo1 = rpc:call(Cp1, dist_ac, info, []), DcInfo2 = rpc:call(Cp2, dist_ac, info, []), @@ -1777,7 +1724,7 @@ distr_changed_tc1(Conf) when is_list(Conf) -> ok; EWa1 -> X1 = io_lib:format("distribution error: Cp1 ~p ",[EWa1]), - test_server:fail(lists:flatten(X1)) + ct:fail(lists:flatten(X1)) end, case lists:sort(Wa2) of @@ -1785,7 +1732,7 @@ distr_changed_tc1(Conf) when is_list(Conf) -> ok; EWa2 -> X2 = io_lib:format("distribution error: Cp2 ~p ",[EWa2]), - test_server:fail(lists:flatten(X2)) + ct:fail(lists:flatten(X2)) end, case lists:sort(Wa3) of @@ -1793,7 +1740,7 @@ distr_changed_tc1(Conf) when is_list(Conf) -> ok; EWa3 -> X3 = io_lib:format("distribution error: Cp3 ~p ",[EWa3]), - test_server:fail(lists:flatten(X3)) + ct:fail(lists:flatten(X3)) end, DcInfo1n = rpc:call(Cp1, dist_ac, info, []), @@ -1815,9 +1762,7 @@ distr_changed_tc1(Conf) when is_list(Conf) -> ok. -distr_changed_tc2(suite) -> []; -distr_changed_tc2(doc) -> ["Test change of distributed parameter, " - "move appls by crashing a node."]; +%% Test change of distributed parameter, move appls by crashing a node. distr_changed_tc2(Conf) when is_list(Conf) -> {OldKernel, OldEnv, {Cp1, Cp2, Cp3}, {Ncp1, _Ncp2, _Ncp3}, Config2} = @@ -1842,21 +1787,16 @@ distr_changed_tc2(Conf) when is_list(Conf) -> rpc:multicall([Cp1, Cp2, Cp3], application_controller, config_change, [OldEnv]), - test_server:sleep(4000), + ct:sleep(4000), stop_node_nice(Cp1), - test_server:sleep(10000), + ct:sleep(10000), -% _DcInfo1 = rpc:call(Cp1, dist_ac, info, []), _DcInfo2 = rpc:call(Cp2, dist_ac, info, []), _DcInfo3 = rpc:call(Cp3, dist_ac, info, []), -% ?t:format(0,"#### DcInfo1 ~n~p~n",[_DcInfo1]), -% DcWa1 = which_applications(Cp1), DcWa2 = which_applications(Cp2), DcWa3 = which_applications(Cp3), -% Wa1 = lists:foldl(fun({A1, _N1, _V1}, AccIn) -> [A1 | AccIn] end, -% [], DcWa1), Wa2 = lists:foldl(fun({A2, _N2, _V2}, AccIn) -> [A2 | AccIn] end, [], DcWa2), Wa3 = lists:foldl(fun({A3, _N3, _V3}, AccIn) -> [A3 | AccIn] end, @@ -1868,7 +1808,7 @@ distr_changed_tc2(Conf) when is_list(Conf) -> ok; EWa2 -> X2 = io_lib:format("distribution error: Cp2 ~p ",[EWa2]), - test_server:fail(lists:flatten(X2)) + ct:fail(lists:flatten(X2)) end, case lists:sort(Wa3) of @@ -1876,12 +1816,12 @@ distr_changed_tc2(Conf) when is_list(Conf) -> ok; EWa3 -> X3 = io_lib:format("distribution error: Cp3 ~p ",[EWa3]), - test_server:fail(lists:flatten(X3)) + ct:fail(lists:flatten(X3)) end, {ok, Cp1} = start_node_boot(Ncp1, Config2, dc), - test_server:sleep(10000), + ct:sleep(10000), _DcInfo1rs = rpc:call(Cp1, dist_ac, info, []), _DcInfo2rs = rpc:call(Cp2, dist_ac, info, []), @@ -1903,7 +1843,7 @@ distr_changed_tc2(Conf) when is_list(Conf) -> ok; EWa1rs -> X1rs = io_lib:format("distribution error: Cp1 ~p ",[EWa1rs]), - test_server:fail(lists:flatten(X1rs)) + ct:fail(lists:flatten(X1rs)) end, case lists:sort(Wa2rs) of @@ -1911,7 +1851,7 @@ distr_changed_tc2(Conf) when is_list(Conf) -> ok; EWa2rs -> X2rs = io_lib:format("distribution error: Cp2 ~p ",[EWa2rs]), - test_server:fail(lists:flatten(X2rs)) + ct:fail(lists:flatten(X2rs)) end, case lists:sort(Wa3rs) of @@ -1919,7 +1859,7 @@ distr_changed_tc2(Conf) when is_list(Conf) -> ok; EWa3rs -> X3rs = io_lib:format("distribution error: Cp3 ~p ",[EWa3rs]), - test_server:fail(lists:flatten(X3rs)) + ct:fail(lists:flatten(X3rs)) end, @@ -1938,15 +1878,12 @@ distr_changed_tc2(Conf) when is_list(Conf) -> %%%----------------------------------------------------------------- %%% Testing of application configuration change %%%----------------------------------------------------------------- -config_change(suite) -> - []; -config_change(doc) -> - ["Test change of application configuration"]; +%% Test change of application configuration. config_change(Conf) when is_list(Conf) -> %% Change to data_dir {ok, CWD} = file:get_cwd(), - DataDir = ?config(data_dir, Conf), + DataDir = proplists:get_value(data_dir, Conf), ok = file:set_cwd(DataDir), %% Find out application data from boot script @@ -2008,10 +1945,7 @@ get_appls([], Res) -> Res. -persistent_env(suite) -> - []; -persistent_env(doc) -> - ["Test set_env/4 and unset_env/3 with persistent true"]; +%% Test set_env/4 and unset_env/3 with persistent true. persistent_env(Conf) when is_list(Conf) -> ok = application:set_env(appinc, own2, persist, [{persistent, true}]), ok = application:set_env(appinc, key1, persist, [{persistent, true}]), @@ -2055,10 +1989,7 @@ persistent_env(Conf) when is_list(Conf) -> %%%----------------------------------------------------------------- %%% Tests the 'shutdown_func' kernel config parameter %%%----------------------------------------------------------------- -shutdown_func(suite) -> - []; -shutdown_func(doc) -> - ["Tests the 'shutdown_func' kernel config parameter"]; +%% Tests the 'shutdown_func' kernel config parameter. shutdown_func(Config) when is_list(Config) -> {ok,Cp1} = start_node(?MODULE_STRING++"_shutdown_func"), wait_for_ready_net(), @@ -2076,10 +2007,10 @@ shutdown_func(Config) when is_list(Config) -> {'DOWN', Mref, _, Pid, noconnection} -> ok after 10000 -> - test_server:fail(timeout) + ct:fail(timeout) end after 10000 -> - test_server:fail(timeout) + ct:fail(timeout) end. @@ -2097,7 +2028,7 @@ do_shutdown(Reason) -> %%% Tests the 'shutdown_timeout' kernel config parameter %%%----------------------------------------------------------------- shutdown_timeout(Config) when is_list(Config) -> - DataDir = ?config(data_dir,Config), + DataDir = proplists:get_value(data_dir,Config), {ok,Cp1} = start_node(?MODULE_STRING++"_shutdown_timeout"), wait_for_ready_net(), ok = rpc:call(Cp1, application, set_env, [kernel, shutdown_timeout, 1000]), @@ -2120,7 +2051,7 @@ shutdown_timeout(Config) when is_list(Config) -> %%% Provokes a (previous) application shutdown deadlock %%%----------------------------------------------------------------- shutdown_deadlock(Config) when is_list(Config) -> - DataDir = ?config(data_dir,Config), + DataDir = proplists:get_value(data_dir,Config), code:add_path(filename:join([DataDir,deadlock])), %% ok = rpc:call(Cp1, application, start, [sasl]), ok = application:start(deadlock), @@ -2145,6 +2076,42 @@ shutdown_deadlock(Config) when is_list(Config) -> %%----------------------------------------------------------------- +%% Relative paths in sys.config +%%----------------------------------------------------------------- +config_relative_paths(Config) -> + Dir = ?config(priv_dir,Config), + SubDir = filename:join(Dir,"subdir"), + Sys = filename:join(SubDir,"sys.config"), + ok = filelib:ensure_dir(Sys), + ok = file:write_file(Sys,"[\"../up.config\",\"current\"].\n"), + + Up = filename:join(Dir,"up.config"), + ok = file:write_file(Up,"[{app1,[{key1,value}]}].\n"), + + {ok,Cwd} = file:get_cwd(), + Current1 = filename:join(Cwd,"current.config"), + ok = file:write_file(Current1,"[{app1,[{key2,value1}]}].\n"), + + N1 = list_to_atom(lists:concat([?FUNCTION_NAME,"_1"])), + {ok,Node1} = start_node(N1,filename:rootname(Sys)), + ok = rpc:call(Node1, application, load, [app1()]), + {ok, value} = rpc:call(Node1, application, get_env,[app1,key1]), + {ok, value1} = rpc:call(Node1, application, get_env,[app1,key2]), + + Current2 = filename:join(SubDir,"current.config"), + ok = file:write_file(Current2,"[{app1,[{key2,value2}]}].\n"), + + N2 = list_to_atom(lists:concat([?FUNCTION_NAME,"_2"])), + {ok, Node2} = start_node(N2,filename:rootname(Sys)), + ok = rpc:call(Node2, application, load, [app1()]), + {ok, value} = rpc:call(Node2, application, get_env,[app1,key1]), + {ok, value2} = rpc:call(Node2, application, get_env,[app1,key2]), + + stop_node_nice([Node1,Node2]), + + ok. + +%%----------------------------------------------------------------- %% Utility functions %%----------------------------------------------------------------- app0() -> @@ -2595,7 +2562,7 @@ is_started(Name, Node) -> false -> false end. -% Waits until application Name is started on at least one node. +%% Waits until application Name is started on at least one node. wait_until_started(Name, Nodes) -> case lists:member(true, lists:map(fun (N) -> @@ -2605,11 +2572,11 @@ wait_until_started(Name, Nodes) -> true -> true; false -> - test_server:sleep(500), + ct:sleep(500), wait_until_started(Name, Nodes) end. -% Waits until application Name is stopped on all nodes. +%% Waits until application Name is stopped on all nodes. wait_until_stopped(Name, Nodes) -> case lists:member(true, lists:map(fun (N) -> @@ -2619,7 +2586,7 @@ wait_until_stopped(Name, Nodes) -> false -> true; true -> - test_server:sleep(500), + ct:sleep(500), wait_until_stopped(Name, Nodes) end. @@ -2661,13 +2628,13 @@ start_node_args(Name, Args) -> start_node_boot_3002(Name, Boot) -> Pa = filename:dirname(code:which(?MODULE)), - ?t:format(0, "start_node_boot ~p~n", - [" -pa " ++ Pa ++ " -env ERL_CRASH_DUMP erl_crash_dump." ++ - atom_to_list(Name) ++ " -boot " ++ Boot ++ - " -sasl dummy \"missing "]), + ct:pal(?HI_VERBOSITY, "start_node_boot ~p~n", + [" -pa " ++ Pa ++ " -env ERL_CRASH_DUMP erl_crash_dump." ++ + atom_to_list(Name) ++ " -boot " ++ Boot ++ + " -sasl dummy \"missing "]), test_server:start_node(Name, slave, [{args, " -pa " ++ Pa ++ - " -env ERL_CRASH_DUMP erl_crash_dump." ++ + " -env ERL_CRASH_DUMP erl_crash_dump." ++ atom_to_list(Name) ++ " -boot " ++ Boot ++ " -sasl dummy \"missing "}]). @@ -2677,18 +2644,20 @@ start_node_boot_config(Name, SysConfigFun, Conf, Boot) -> start_node_boot(Name, Config, Boot) -> Pa = filename:dirname(code:which(?MODULE)), - ?t:format(0, "start_node_boot ~p~n",[" -pa " ++ Pa ++ " -config " ++ Config ++ - " -boot " ++ atom_to_list(Boot)]), - test_server:start_node(Name, slave, [{args, " -pa " ++ Pa ++ " -config " ++ Config ++ - " -boot " ++ atom_to_list(Boot)}]). + ct:pal(?HI_VERBOSITY, + "start_node_boot ~p~n",[" -pa " ++ Pa ++ " -config " ++ Config ++ + " -boot " ++ atom_to_list(Boot)]), + test_server:start_node(Name, slave, + [{args, " -pa " ++ Pa ++ " -config " ++ Config ++ + " -boot " ++ atom_to_list(Boot)}]). start_node_config_sf(Name, SysConfigFun, Conf) -> ConfigFile = write_config_file(SysConfigFun, Conf), - DataDir = ?config(data_dir, Conf), % is it used? + DataDir = proplists:get_value(data_dir, Conf), % is it used? start_node(Name, ConfigFile, " -pa " ++ DataDir). write_config_file(SysConfigFun, Conf) -> - Dir = ?config(priv_dir, Conf), + Dir = proplists:get_value(priv_dir, Conf), {ok, Fd} = file:open(filename:join(Dir, "sys.config"), [write]), SysConfigFun(Fd), file:close(Fd), @@ -2699,10 +2668,7 @@ node_names(Names, Config) -> node_name(Name, Config) -> U = "_", - {{Y,M,D}, {H,Min,S}} = calendar:now_to_local_time(now()), - Date = io_lib:format("~4w_~2..0w_~2..0w__~2..0w_~2..0w_~2..0w", - [Y,M,D, H,Min,S]), - L = lists:flatten(Date), + L = integer_to_list(erlang:unique_integer([positive])), lists:concat([Name,U,?testcase,U,U,L]). stop_node_nice(Node) when is_atom(Node) -> @@ -2715,8 +2681,8 @@ get_start_type(Expected) -> get_start_type(Expected, 30*5, #st{}). get_start_type(_Expected, 0, Ack) -> - test_server:format("====== ~p ======~n", [Ack]), - test_server:fail(not_valid_start_type); + io:format("====== ~p ======~n", [Ack]), + ct:fail(not_valid_start_type); get_start_type(Expected, Times, Ack0) -> #st{normal = N0, local = L0, takeover = T0, failover = F0} = Ack0, global:send(st_type, {st, read, self()}), @@ -2762,13 +2728,13 @@ get_start_phase(Expected) -> Expected -> ok; {sp, T1, I1, So1, Sp1, G1} -> - test_server:format("=============== {sp,T,I,So,Sp,G} ~p ~n",[" "]), - test_server:format("=========== got ~p ~n", + io:format("=============== {sp,T,I,So,Sp,G} ~p ~n",[" "]), + io:format("=========== got ~p ~n", [{sp, T1, I1, So1, Sp1, G1}]), - test_server:format("====== expected ~p ~n", [Expected]), - test_server:fail(not_valid_start_phase) + io:format("====== expected ~p ~n", [Expected]), + ct:fail(not_valid_start_phase) after 5000 -> - test_server:fail(not_valid_start_phase) + ct:fail(not_valid_start_phase) end. start_phase() -> @@ -2799,10 +2765,10 @@ get_conf_change(Expected) -> {cc, Expected} -> ok; {cc, List} -> - test_server:format("====== ~p ======~n",[{cc, List}]), - test_server:fail(not_valid_conf_change) + io:format("====== ~p ======~n",[{cc, List}]), + ct:fail(not_valid_conf_change) after 5000 -> - test_server:fail(not_valid_conf_change_to) + ct:fail(not_valid_conf_change_to) end. conf_change() -> @@ -2898,7 +2864,7 @@ create_script_3002(ScriptName) -> distr_changed_prep(Conf) when is_list(Conf) -> - % Write .app files + %% Write .app files {ok, Fd1} = file:open("app1.app", [write]), w_app1(Fd1), file:close(Fd1), @@ -2919,7 +2885,7 @@ distr_changed_prep(Conf) when is_list(Conf) -> file:close(Fd6), - % Create the .app files and the boot script + %% Create the .app files and the boot script {{KernelVer,StdlibVer}, _} = create_script_dc("dc"), case is_real_system(KernelVer, StdlibVer) of @@ -2935,13 +2901,13 @@ distr_changed_prep(Conf) when is_list(Conf) -> NoSyncTime = config_fun_fast(config_dc(NodeNames)), WithSyncTime = config_fun(config_dc(NodeNames)), - Dir = ?config(priv_dir,Conf), + Dir = proplists:get_value(priv_dir,Conf), {ok, Fd_dc2} = file:open(filename:join(Dir, "sys2.config"), [write]), (config_dc2(NodeNames))(Fd_dc2), file:close(Fd_dc2), Config2 = filename:join(Dir, "sys2"), - % Test [cp1, cp2, cp3] + %% Test [cp1, cp2, cp3] {ok, Cp1} = start_node_boot_config(Ncp1, NoSyncTime, Conf, dc), {ok, Cp2} = start_node_boot_config(Ncp2, NoSyncTime, Conf, dc), {ok, Cp3} = start_node_boot_config(Ncp3, WithSyncTime, Conf, dc), diff --git a/lib/kernel/test/application_SUITE_data/app_start_error.erl b/lib/kernel/test/application_SUITE_data/app_start_error.erl index cfe3508eb3..aedcb537d8 100644 --- a/lib/kernel/test/application_SUITE_data/app_start_error.erl +++ b/lib/kernel/test/application_SUITE_data/app_start_error.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2009. All Rights Reserved. +%% Copyright Ericsson AB 1998-2016. 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/lib/kernel/test/application_SUITE_data/group_leader.erl b/lib/kernel/test/application_SUITE_data/group_leader.erl index 08c5b43808..f18cef179b 100644 --- a/lib/kernel/test/application_SUITE_data/group_leader.erl +++ b/lib/kernel/test/application_SUITE_data/group_leader.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2009. All Rights Reserved. +%% Copyright Ericsson AB 2005-2016. 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/lib/kernel/test/application_SUITE_data/group_leader_sup.erl b/lib/kernel/test/application_SUITE_data/group_leader_sup.erl index 04bb0538fe..6e0df632c3 100644 --- a/lib/kernel/test/application_SUITE_data/group_leader_sup.erl +++ b/lib/kernel/test/application_SUITE_data/group_leader_sup.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2009. All Rights Reserved. +%% Copyright Ericsson AB 2005-2016. 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/lib/kernel/test/application_SUITE_data/trans_abnormal_sup.erl b/lib/kernel/test/application_SUITE_data/trans_abnormal_sup.erl index d060347aff..bc2bf84089 100644 --- a/lib/kernel/test/application_SUITE_data/trans_abnormal_sup.erl +++ b/lib/kernel/test/application_SUITE_data/trans_abnormal_sup.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2009. All Rights Reserved. +%% Copyright Ericsson AB 1998-2016. 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/lib/kernel/test/application_SUITE_data/trans_normal_sup.erl b/lib/kernel/test/application_SUITE_data/trans_normal_sup.erl index 48eb52ddcf..c839888e39 100644 --- a/lib/kernel/test/application_SUITE_data/trans_normal_sup.erl +++ b/lib/kernel/test/application_SUITE_data/trans_normal_sup.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2009. All Rights Reserved. +%% Copyright Ericsson AB 1998-2016. 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/lib/kernel/test/application_SUITE_data/transient.erl b/lib/kernel/test/application_SUITE_data/transient.erl index 1f38b4803a..83cdacf673 100644 --- a/lib/kernel/test/application_SUITE_data/transient.erl +++ b/lib/kernel/test/application_SUITE_data/transient.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2009. All Rights Reserved. +%% Copyright Ericsson AB 1998-2016. 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/lib/kernel/test/bif_SUITE.erl b/lib/kernel/test/bif_SUITE.erl index c369dca4e1..2369dd8b71 100644 --- a/lib/kernel/test/bif_SUITE.erl +++ b/lib/kernel/test/bif_SUITE.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2012. All Rights Reserved. +%% Copyright Ericsson AB 1998-2016. 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/. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at %% -%% 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. +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% @@ -21,35 +22,33 @@ init_per_group/2,end_per_group/2]). -export([ - spawn1/1, spawn2/1, spawn3/1, spawn4/1, + spawn1/1, spawn2/1, spawn3/1, spawn4/1, - - spawn_link1/1, spawn_link2/1, spawn_link3/1, spawn_link4/1, - - spawn_opt2/1, spawn_opt3/1, spawn_opt4/1, spawn_opt5/1, + spawn_link1/1, spawn_link2/1, spawn_link3/1, spawn_link4/1, - spawn_failures/1, - run_fun/1, - wilderness/1]). + spawn_opt2/1, spawn_opt3/1, spawn_opt4/1, spawn_opt5/1, --export([init_per_testcase/2, end_per_testcase/2]). + spawn_failures/1, --include_lib("test_server/include/test_server.hrl"). + run_fun/1, + decode_packet_delim/1, + wilderness/1]). + +-export([init_per_testcase/2, end_per_testcase/2]). -% Default timetrap timeout (set in init_per_testcase). --define(default_timeout, ?t:minutes(1)). +-include_lib("common_test/include/ct.hrl"). init_per_testcase(_Case, Config) -> - ?line Dog = ?t:timetrap(?default_timeout), - [{watchdog, Dog} | Config]. -end_per_testcase(_Case, Config) -> - Dog = ?config(watchdog, Config), - test_server:timetrap_cancel(Dog), + Config. + +end_per_testcase(_Case, _Config) -> ok. -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap,{minutes,1}}]. all() -> [{group, spawn_tests}, {group, spawn_link_tests}, @@ -75,360 +74,333 @@ end_per_group(_GroupName, Config) -> Config. -spawn1(doc) -> ["Test spawn/1"]; -spawn1(suite) -> - []; +%% Test spawn/1. spawn1(Config) when is_list(Config) -> - ?line Node = node(), - ?line Parent = self(), - ?line {_, _, FA, _} = fetch_proc_vals(self()), - - % spawn - ?line P = spawn(fun() -> Parent ! {self(), fetch_proc_vals(self())} end), - ?line receive - {P, PV} -> - ?line Node = node(P), - ?line check_proc_vals(false, normal, FA, 0, PV) - end, + Node = node(), + Parent = self(), + {_, _, FA, _} = fetch_proc_vals(self()), + + %% spawn + P = spawn(fun() -> Parent ! {self(), fetch_proc_vals(self())} end), + receive + {P, PV} -> + Node = node(P), + check_proc_vals(false, normal, FA, 0, PV) + end, ok. -spawn2(doc) -> ["Test spawn/2"]; -spawn2(suite) -> - []; +%% Test spawn/2. spawn2(Config) when is_list(Config) -> - ?line {ok, Node} = start_node(spawn2), + {ok, Node} = start_node(spawn2), - ?line Parent = self(), - ?line {_, _, FA, _} = fetch_proc_vals(self()), + Parent = self(), + {_, _, FA, _} = fetch_proc_vals(self()), - % spawn_link - ?line P = spawn(Node, - fun() -> Parent ! {self(), fetch_proc_vals(self())} end), - ?line receive - {P, PV} -> - ?line Node = node(P), - ?line check_proc_vals(false, normal, FA, 0, PV) - end, + %% spawn_link + P = spawn(Node, + fun() -> Parent ! {self(), fetch_proc_vals(self())} end), + receive + {P, PV} -> + Node = node(P), + check_proc_vals(false, normal, FA, 0, PV) + end, - ?line true = stop_node(Node), + true = stop_node(Node), ok. -spawn3(doc) -> ["Test spawn/3"]; -spawn3(suite) -> - []; +%% Test spawn/3. spawn3(Config) when is_list(Config) -> - ?line Node = node(), - - ?line Parent = self(), - ?line {_, _, FA, _} = fetch_proc_vals(self()), - - % spawn_link - ?line P = spawn(?MODULE, - run_fun, - [fun() -> - Parent ! {self(), fetch_proc_vals(self())} - end]), - ?line receive - {P, PV} -> - ?line Node = node(P), - ?line check_proc_vals(false, normal, FA, 0, PV) - end, + Node = node(), + + Parent = self(), + {_, _, FA, _} = fetch_proc_vals(self()), + + %% spawn_link + P = spawn(?MODULE, + run_fun, + [fun() -> + Parent ! {self(), fetch_proc_vals(self())} + end]), + receive + {P, PV} -> + Node = node(P), + check_proc_vals(false, normal, FA, 0, PV) + end, ok. -spawn4(doc) -> ["Test spawn/4"]; -spawn4(suite) -> - []; +%% Test spawn/4. spawn4(Config) when is_list(Config) -> - ?line {ok, Node} = start_node(spawn4), - - ?line Parent = self(), - ?line {_, _, FA, _} = fetch_proc_vals(self()), - - % spawn_link - ?line P = spawn(Node, - ?MODULE, - run_fun, - [fun() -> - Parent ! {self(), fetch_proc_vals(self())} - end]), - ?line receive - {P, PV} -> - ?line Node = node(P), - ?line check_proc_vals(false, normal, FA, 0, PV) - end, - - ?line true = stop_node(Node), + {ok, Node} = start_node(spawn4), + + Parent = self(), + {_, _, FA, _} = fetch_proc_vals(self()), + + %% spawn_link + P = spawn(Node, + ?MODULE, + run_fun, + [fun() -> + Parent ! {self(), fetch_proc_vals(self())} + end]), + receive + {P, PV} -> + Node = node(P), + check_proc_vals(false, normal, FA, 0, PV) + end, + + true = stop_node(Node), ok. -spawn_link1(doc) -> ["Test spawn_link/1"]; -spawn_link1(suite) -> - []; +%% Test spawn_link/1. spawn_link1(Config) when is_list(Config) -> - ?line Node = node(), - ?line Parent = self(), - ?line {_, _, FA, _} = fetch_proc_vals(self()), - - % spawn_link - ?line P = spawn_link(fun() -> Parent ! {self(), fetch_proc_vals(self())} end), - ?line receive - {P, PV} -> - ?line Node = node(P), - ?line check_proc_vals(true, normal, FA, 0, PV) - end, + Node = node(), + Parent = self(), + {_, _, FA, _} = fetch_proc_vals(self()), + + %% spawn_link + P = spawn_link(fun() -> Parent ! {self(), fetch_proc_vals(self())} end), + receive + {P, PV} -> + Node = node(P), + check_proc_vals(true, normal, FA, 0, PV) + end, ok. -spawn_link2(doc) -> ["Test spawn_link/2"]; -spawn_link2(suite) -> - []; +%% Test spawn_link/2. spawn_link2(Config) when is_list(Config) -> - ?line {ok, Node} = start_node(spawn_link2), + {ok, Node} = start_node(spawn_link2), - ?line Parent = self(), - ?line {_, _, FA, _} = fetch_proc_vals(self()), + Parent = self(), + {_, _, FA, _} = fetch_proc_vals(self()), - % spawn_link - ?line P = spawn_link(Node, - fun() -> Parent ! {self(), fetch_proc_vals(self())} end), - ?line receive - {P, PV} -> - ?line Node = node(P), - ?line check_proc_vals(true, normal, FA, 0, PV) - end, + %% spawn_link + P = spawn_link(Node, + fun() -> Parent ! {self(), fetch_proc_vals(self())} end), + receive + {P, PV} -> + Node = node(P), + check_proc_vals(true, normal, FA, 0, PV) + end, - ?line true = stop_node(Node), + true = stop_node(Node), ok. -spawn_link3(doc) -> ["Test spawn_link/3"]; -spawn_link3(suite) -> - []; +%% Test spawn_link/3. spawn_link3(Config) when is_list(Config) -> - ?line Node = node(), - - ?line Parent = self(), - ?line {_, _, FA, _} = fetch_proc_vals(self()), - - % spawn_link - ?line P = spawn_link(?MODULE, - run_fun, - [fun() -> - Parent ! {self(), fetch_proc_vals(self())} - end]), - ?line receive - {P, PV} -> - ?line Node = node(P), - ?line check_proc_vals(true, normal, FA, 0, PV) - end, + Node = node(), + + Parent = self(), + {_, _, FA, _} = fetch_proc_vals(self()), + + %% spawn_link + P = spawn_link(?MODULE, + run_fun, + [fun() -> + Parent ! {self(), fetch_proc_vals(self())} + end]), + receive + {P, PV} -> + Node = node(P), + check_proc_vals(true, normal, FA, 0, PV) + end, ok. -spawn_link4(doc) -> ["Test spawn_link/4"]; -spawn_link4(suite) -> - []; +%% Test spawn_link/4. spawn_link4(Config) when is_list(Config) -> - ?line {ok, Node} = start_node(spawn_link4), - - ?line Parent = self(), - ?line {_, _, FA, _} = fetch_proc_vals(self()), - - % spawn_link - ?line P = spawn_link(Node, - ?MODULE, - run_fun, - [fun() -> - Parent ! {self(), fetch_proc_vals(self())} - end]), - ?line receive - {P, PV} -> - ?line Node = node(P), - ?line check_proc_vals(true, normal, FA, 0, PV) - end, - - ?line true = stop_node(Node), + {ok, Node} = start_node(spawn_link4), + + Parent = self(), + {_, _, FA, _} = fetch_proc_vals(self()), + + %% spawn_link + P = spawn_link(Node, + ?MODULE, + run_fun, + [fun() -> + Parent ! {self(), fetch_proc_vals(self())} + end]), + receive + {P, PV} -> + Node = node(P), + check_proc_vals(true, normal, FA, 0, PV) + end, + + true = stop_node(Node), ok. -spawn_opt2(doc) -> ["Test spawn_opt/2"]; -spawn_opt2(suite) -> - []; +%% Test spawn_opt/2. spawn_opt2(Config) when is_list(Config) -> - ?line Node = node(), - ?line Parent = self(), - ?line {_, _, FA, _} = fetch_proc_vals(self()), - - ?line P1 = spawn_opt(fun() -> - Parent ! {self(), fetch_proc_vals(self())} - end, - [{fullsweep_after, 0},{min_heap_size, 1000}, - link, {priority, max}]), - ?line receive - {P1, PV1} -> - ?line Node = node(P1), - ?line check_proc_vals(true, max, 0, 1000, PV1) - end, - ?line P2 = spawn_opt(fun() -> Parent ! {self(), fetch_proc_vals(self())} end, - [{min_heap_size, 10}]), - ?line receive - {P2, PV2} -> - ?line Node = node(P2), - ?line check_proc_vals(false, normal, FA, 10, PV2) - end, + Node = node(), + Parent = self(), + {_, _, FA, _} = fetch_proc_vals(self()), + + P1 = spawn_opt(fun() -> + Parent ! {self(), fetch_proc_vals(self())} + end, + [{fullsweep_after, 0},{min_heap_size, 1000}, + link, {priority, max}]), + receive + {P1, PV1} -> + Node = node(P1), + check_proc_vals(true, max, 0, 1000, PV1) + end, + P2 = spawn_opt(fun() -> Parent ! {self(), fetch_proc_vals(self())} end, + [{min_heap_size, 10}]), + receive + {P2, PV2} -> + Node = node(P2), + check_proc_vals(false, normal, FA, 10, PV2) + end, ok. -spawn_opt3(doc) -> ["Test spawn_opt/3"]; -spawn_opt3(suite) -> - []; +%% Test spawn_opt/3. spawn_opt3(Config) when is_list(Config) -> - ?line {ok, Node} = start_node(spawn_opt3), - ?line Parent = self(), - ?line {_, _, FA, _} = fetch_proc_vals(self()), - ?line P1 = spawn_opt(Node, - fun() -> - Parent ! {self(), fetch_proc_vals(self())} - end, - [{fullsweep_after,0}, {min_heap_size,1000}, - link, {priority, max}]), - ?line receive - {P1, PV1} -> - ?line Node = node(P1), - ?line check_proc_vals(true, max, 0, 1000, PV1) - end, - ?line P2 = spawn_opt(Node, - fun() -> Parent ! {self(), fetch_proc_vals(self())} end, - [{min_heap_size, 10}]), - ?line receive - {P2, PV2} -> - ?line Node = node(P2), - ?line check_proc_vals(false, normal, FA, 10, PV2) - end, - ?line true = stop_node(Node), + {ok, Node} = start_node(spawn_opt3), + Parent = self(), + {_, _, FA, _} = fetch_proc_vals(self()), + P1 = spawn_opt(Node, + fun() -> + Parent ! {self(), fetch_proc_vals(self())} + end, + [{fullsweep_after,0}, {min_heap_size,1000}, + link, {priority, max}]), + receive + {P1, PV1} -> + Node = node(P1), + check_proc_vals(true, max, 0, 1000, PV1) + end, + P2 = spawn_opt(Node, + fun() -> Parent ! {self(), fetch_proc_vals(self())} end, + [{min_heap_size, 10}]), + receive + {P2, PV2} -> + Node = node(P2), + check_proc_vals(false, normal, FA, 10, PV2) + end, + true = stop_node(Node), ok. -spawn_opt4(doc) -> ["Test spawn_opt/4"]; -spawn_opt4(suite) -> - []; +%% Test spawn_opt/4. spawn_opt4(Config) when is_list(Config) -> - ?line Node = node(), - ?line Parent = self(), - ?line {_, _, FA, _} = fetch_proc_vals(self()), - ?line P1 = spawn_opt(?MODULE, - run_fun, - [fun() -> - Parent ! {self(), fetch_proc_vals(self())} - end], - [{fullsweep_after,0}, {min_heap_size,1000}, - link, {priority, max}]), - ?line receive - {P1, PV1} -> - ?line Node = node(P1), - ?line check_proc_vals(true, max, 0, 1000, PV1) - end, - ?line P2 = spawn_opt(?MODULE, - run_fun, - [fun() -> - Parent ! {self(), fetch_proc_vals(self())} - end], - [{min_heap_size, 10}]), - ?line receive - {P2, PV2} -> - ?line Node = node(P2), - ?line check_proc_vals(false, normal, FA, 10, PV2) - end, + Node = node(), + Parent = self(), + {_, _, FA, _} = fetch_proc_vals(self()), + P1 = spawn_opt(?MODULE, + run_fun, + [fun() -> + Parent ! {self(), fetch_proc_vals(self())} + end], + [{fullsweep_after,0}, {min_heap_size,1000}, + link, {priority, max}]), + receive + {P1, PV1} -> + Node = node(P1), + check_proc_vals(true, max, 0, 1000, PV1) + end, + P2 = spawn_opt(?MODULE, + run_fun, + [fun() -> + Parent ! {self(), fetch_proc_vals(self())} + end], + [{min_heap_size, 10}]), + receive + {P2, PV2} -> + Node = node(P2), + check_proc_vals(false, normal, FA, 10, PV2) + end, ok. -spawn_opt5(doc) -> ["Test spawn_opt/5"]; -spawn_opt5(suite) -> - []; +%% Test spawn_opt/5. spawn_opt5(Config) when is_list(Config) -> - ?line {ok, Node} = start_node(spawn_opt5), - ?line Parent = self(), - ?line {_, _, FA, _} = fetch_proc_vals(self()), - ?line P1 = spawn_opt(Node, - ?MODULE, - run_fun, - [fun() -> - Parent ! {self(), fetch_proc_vals(self())} - end], - [{fullsweep_after,0}, {min_heap_size,1000}, - link, {priority, max}]), - ?line receive - {P1, PV1} -> - ?line Node = node(P1), - ?line check_proc_vals(true, max, 0, 1000, PV1) - end, - ?line P2 = spawn_opt(Node, - ?MODULE, - run_fun, - [fun() -> - Parent ! {self(), fetch_proc_vals(self())} - end], - [{min_heap_size, 10}]), - ?line receive - {P2, PV2} -> - ?line Node = node(P2), - ?line check_proc_vals(false, normal, FA, 10, PV2) - end, - ?line true = stop_node(Node), + {ok, Node} = start_node(spawn_opt5), + Parent = self(), + {_, _, FA, _} = fetch_proc_vals(self()), + P1 = spawn_opt(Node, + ?MODULE, + run_fun, + [fun() -> + Parent ! {self(), fetch_proc_vals(self())} + end], + [{fullsweep_after,0}, {min_heap_size,1000}, + link, {priority, max}]), + receive + {P1, PV1} -> + Node = node(P1), + check_proc_vals(true, max, 0, 1000, PV1) + end, + P2 = spawn_opt(Node, + ?MODULE, + run_fun, + [fun() -> + Parent ! {self(), fetch_proc_vals(self())} + end], + [{min_heap_size, 10}]), + receive + {P2, PV2} -> + Node = node(P2), + check_proc_vals(false, normal, FA, 10, PV2) + end, + true = stop_node(Node), ok. -spawn_failures(doc) -> - ["Test failure behavior of spawn bifs"]; -spawn_failures(suite) -> - []; +%% Test failure behavior of spawn bifs. spawn_failures(Config) when is_list(Config) -> - ?line ThisNode = node(), - ?line {ok, Node} = start_node(spawn_remote_failure), - - % unknown nodes - test_server:format("Testing unknown nodes~n", []), - ?line CrashPid1 = (catch spawn_opt('unknown@node', - erlang, - nodes, - [], - [])), - ?line true = is_pid(CrashPid1), - ?line ThisNode = node(CrashPid1), - ?line CrashPid2 = (catch spawn_opt('unknown@node', - fun () -> erlang:nodes() end, - [])), - ?line true = is_pid(CrashPid2), - ?line ThisNode = node(CrashPid2), - - ?line CrashPid3 = (catch spawn('unknown@node', - erlang, - nodes, - [])), - ?line true = is_pid(CrashPid3), - ?line ThisNode = node(CrashPid3), - ?line CrashPid4 = (catch spawn('unknown@node', - fun () -> erlang:nodes() end)), - ?line true = is_pid(CrashPid4), - ?line ThisNode = node(CrashPid4), - - ?line OTE = process_flag(trap_exit,true), - ?line CrashPid5 = (catch spawn_link('unknown@node', - erlang, - nodes, - [])), + ThisNode = node(), + {ok, Node} = start_node(spawn_remote_failure), + + %% unknown nodes + io:format("Testing unknown nodes~n", []), + CrashPid1 = (catch spawn_opt('unknown@node', + erlang, + nodes, + [], + [])), + true = is_pid(CrashPid1), + ThisNode = node(CrashPid1), + CrashPid2 = (catch spawn_opt('unknown@node', + fun () -> erlang:nodes() end, + [])), + true = is_pid(CrashPid2), + ThisNode = node(CrashPid2), + + CrashPid3 = (catch spawn('unknown@node', + erlang, + nodes, + [])), + true = is_pid(CrashPid3), + ThisNode = node(CrashPid3), + CrashPid4 = (catch spawn('unknown@node', + fun () -> erlang:nodes() end)), + true = is_pid(CrashPid4), + ThisNode = node(CrashPid4), + + OTE = process_flag(trap_exit,true), + CrashPid5 = (catch spawn_link('unknown@node', + erlang, + nodes, + [])), receive {'EXIT', CrashPid5, noconnection} -> - ?line true = is_pid(CrashPid5), - ?line ThisNode = node(CrashPid5) + true = is_pid(CrashPid5), + ThisNode = node(CrashPid5) end, - ?line CrashPid6 = (catch spawn_link('unknown@node', - fun () -> erlang:nodes() end)), + CrashPid6 = (catch spawn_link('unknown@node', + fun () -> erlang:nodes() end)), receive {'EXIT', CrashPid6, noconnection} -> - ?line true = is_pid(CrashPid6), - ?line ThisNode = node(CrashPid6) + true = is_pid(CrashPid6), + ThisNode = node(CrashPid6) end, process_flag(trap_exit,OTE), case OTE of false -> receive {'EXIT', P, R} -> - ?line test_server:fail({'EXIT', P, R}) + ct:fail({'EXIT', P, R}) after 0 -> ok end; @@ -436,123 +408,125 @@ spawn_failures(Config) when is_list(Config) -> ok end, - % bad node - test_server:format("Testing bad nodes~n", []), - ?line {'EXIT', {badarg, _}} = (catch spawn_opt("Node",erlang,nodes,[],[])), - ?line {'EXIT', {badarg, _}} = (catch spawn_opt("Node", - fun () -> - erlang:nodes() - end, - [])), - ?line {'EXIT', {badarg, _}} = (catch spawn_link("Node", - fun () -> - erlang:nodes() - end)), - ?line {'EXIT', {badarg, _}} = (catch spawn("Node",erlang,nodes,[])), - ?line {'EXIT', {badarg, _}} = (catch spawn("Node", - fun () -> - erlang:nodes() - end)), - - % bad module - test_server:format("Testing bad modules~n", []), - ?line {'EXIT', {badarg, _}} = (catch spawn_opt(Node,"erlang",nodes,[],[])), - ?line {'EXIT', {badarg, _}} = (catch spawn_opt("erlang",nodes,[],[])), - ?line {'EXIT', {badarg, _}} = (catch spawn_link(Node,"erlang",nodes,[])), - ?line {'EXIT', {badarg, _}} = (catch spawn_link("erlang",nodes,[])), - ?line {'EXIT', {badarg, _}} = (catch spawn(Node,"erlang",nodes,[])), - ?line {'EXIT', {badarg, _}} = (catch spawn("erlang",nodes,[])), - - % bad function - test_server:format("Testing bad functions~n", []), - ?line {'EXIT', {badarg, _}} = (catch spawn_opt(Node,erlang,"nodes",[],[])), - ?line {'EXIT', {badarg, _}} = (catch spawn_opt(Node,not_a_fun,[])), - ?line {'EXIT', {badarg, _}} = (catch spawn_opt(erlang,"nodes",[],[])), - ?line {'EXIT', {badarg, _}} = (catch spawn_opt(not_a_fun,[])), - ?line {'EXIT', {badarg, _}} = (catch spawn_link(Node,erlang,"nodes",[])), - ?line {'EXIT', {badarg, _}} = (catch spawn_link(Node,not_a_fun)), - ?line {'EXIT', {badarg, _}} = (catch spawn_link(erlang,"nodes",[])), - ?line {'EXIT', {badarg, _}} = (catch spawn_link(not_a_fun)), - ?line {'EXIT', {badarg, _}} = (catch spawn(Node,erlang,"nodes",[])), - ?line {'EXIT', {badarg, _}} = (catch spawn(Node,not_a_fun)), - ?line {'EXIT', {badarg, _}} = (catch spawn(erlang,"nodes",[])), - ?line {'EXIT', {badarg, _}} = (catch spawn(not_a_fun)), - - - % bad argument - test_server:format("Testing bad arguments~n", []), - ?line {'EXIT', {badarg, _}} = (catch spawn_opt(Node,erlang,nodes,[a|b],[])), - ?line {'EXIT', {badarg, _}} = (catch spawn_opt(erlang,nodes,[a|b],[])), - ?line {'EXIT', {badarg, _}} = (catch spawn_link(Node,erlang,nodes,[a|b])), - ?line {'EXIT', {badarg, _}} = (catch spawn_link(erlang,nodes,[a|b])), - ?line {'EXIT', {badarg, _}} = (catch spawn(Node,erlang,nodes,[a|b])), - ?line {'EXIT', {badarg, _}} = (catch spawn(erlang,nodes,[a|b])), - - % bad option - test_server:format("Testing bad options~n", []), - ?line {'EXIT', {badarg, _}} = (catch spawn_opt(Node,erlang,nodes,[],[a|b])), - ?line {'EXIT', {badarg, _}} = (catch spawn_opt(erlang,nodes,[],[a|b])), - - - ?line true = stop_node(Node), + %% bad node + io:format("Testing bad nodes~n", []), + {'EXIT', {badarg, _}} = (catch spawn_opt("Node",erlang,nodes,[],[])), + {'EXIT', {badarg, _}} = (catch spawn_opt("Node", + fun () -> + erlang:nodes() + end, + [])), + {'EXIT', {badarg, _}} = (catch spawn_link("Node", + fun () -> + erlang:nodes() + end)), + {'EXIT', {badarg, _}} = (catch spawn("Node",erlang,nodes,[])), + {'EXIT', {badarg, _}} = (catch spawn("Node", + fun () -> + erlang:nodes() + end)), + + %% bad module + io:format("Testing bad modules~n", []), + {'EXIT', {badarg, _}} = (catch spawn_opt(Node,"erlang",nodes,[],[])), + {'EXIT', {badarg, _}} = (catch spawn_opt("erlang",nodes,[],[])), + {'EXIT', {badarg, _}} = (catch spawn_link(Node,"erlang",nodes,[])), + {'EXIT', {badarg, _}} = (catch spawn_link("erlang",nodes,[])), + {'EXIT', {badarg, _}} = (catch spawn(Node,"erlang",nodes,[])), + {'EXIT', {badarg, _}} = (catch spawn("erlang",nodes,[])), + + %% bad function + io:format("Testing bad functions~n", []), + {'EXIT', {badarg, _}} = (catch spawn_opt(Node,erlang,"nodes",[],[])), + {'EXIT', {badarg, _}} = (catch spawn_opt(Node,not_a_fun,[])), + {'EXIT', {badarg, _}} = (catch spawn_opt(erlang,"nodes",[],[])), + {'EXIT', {badarg, _}} = (catch spawn_opt(not_a_fun,[])), + {'EXIT', {badarg, _}} = (catch spawn_link(Node,erlang,"nodes",[])), + {'EXIT', {badarg, _}} = (catch spawn_link(Node,not_a_fun)), + {'EXIT', {badarg, _}} = (catch spawn_link(erlang,"nodes",[])), + {'EXIT', {badarg, _}} = (catch spawn_link(not_a_fun)), + {'EXIT', {badarg, _}} = (catch spawn(Node,erlang,"nodes",[])), + {'EXIT', {badarg, _}} = (catch spawn(Node,not_a_fun)), + {'EXIT', {badarg, _}} = (catch spawn(erlang,"nodes",[])), + {'EXIT', {badarg, _}} = (catch spawn(not_a_fun)), + + + %% bad argument + io:format("Testing bad arguments~n", []), + {'EXIT', {badarg, _}} = (catch spawn_opt(Node,erlang,nodes,[a|b],[])), + {'EXIT', {badarg, _}} = (catch spawn_opt(erlang,nodes,[a|b],[])), + {'EXIT', {badarg, _}} = (catch spawn_link(Node,erlang,nodes,[a|b])), + {'EXIT', {badarg, _}} = (catch spawn_link(erlang,nodes,[a|b])), + {'EXIT', {badarg, _}} = (catch spawn(Node,erlang,nodes,[a|b])), + {'EXIT', {badarg, _}} = (catch spawn(erlang,nodes,[a|b])), + + %% bad option + io:format("Testing bad options~n", []), + {'EXIT', {badarg, _}} = (catch spawn_opt(Node,erlang,nodes,[],[a|b])), + {'EXIT', {badarg, _}} = (catch spawn_opt(erlang,nodes,[],[a|b])), + + + true = stop_node(Node), ok. check_proc_vals(Link, Priority, FullsweepAfter, MinHeapSize, {Ls, P, FA, HS}) -> - ?line Link = lists:member(self(), Ls), - ?line Priority = P, + Link = lists:member(self(), Ls), + Priority = P, FullsweepAfter = FA, true = (HS >= MinHeapSize), - ?line ok. + ok. fetch_proc_vals(Pid) -> - ?line PI = process_info(Pid), - ?line {value,{links, Ls}} = lists:keysearch(links, 1, PI), - ?line {value,{priority,P}} = lists:keysearch(priority, 1, PI), + PI = process_info(Pid), + {value,{links, Ls}} = lists:keysearch(links, 1, PI), + {value,{priority,P}} = lists:keysearch(priority, 1, PI), {value,{garbage_collection,Gs}} = lists:keysearch(garbage_collection, 1, PI), {value,{fullsweep_after,FA}} = lists:keysearch(fullsweep_after, 1, Gs), {value,{heap_size,HS}} = lists:keysearch(heap_size, 1, PI), - ?line {Ls, P, FA, HS}. - -% This testcase should probably be moved somewhere else -wilderness(doc) -> - ["Test that memory allocation command line options affecting the" - "wilderness of the heap are interpreted correct by the emulator "]; -wilderness(suite) -> - []; + {Ls, P, FA, HS}. + +%% Test erlang:packet_delim/3 with {line_delimiter,0} option. +decode_packet_delim(Config) when is_list(Config) -> + {ok,<<"abc",0>>,<<"efg",0>>} = + erlang:decode_packet(line, <<"abc",0,"efg",0>>, [{line_delimiter, 0}]), + {more, undefined} = erlang:decode_packet(line, <<"abc",0,"efg",0>>, []). + +%% This testcase should probably be moved somewhere else + +%% Test that memory allocation command line options affecting the +%% wilderness of the heap are interpreted correct by the emulator. wilderness(Config) when is_list(Config) -> - ?line Dog = ?t:timetrap(?default_timeout), - ?line OKParams = {512, 8}, - ?line Alloc = erlang:system_info(allocator), - ?line test_server:format("Test server allocator info:~n~p", [Alloc]), + OKParams = {512, 8}, + Alloc = erlang:system_info(allocator), + io:format("Test server allocator info:~n~p", [Alloc]), Result = case Alloc of {Allocator, _, _, _} when Allocator == glibc; Allocator == dlmalloc -> - ?line run_wilderness_test(OKParams, OKParams), - ?line {comment, - "Allocator used: " ++ atom_to_list(Allocator)}; + run_wilderness_test(OKParams, OKParams), + {comment, + "Allocator used: " ++ atom_to_list(Allocator)}; {OtherAllocator, _, _, _} -> - ?line {skipped, - "Only run when glibc is used. " - "Allocator used: " - ++ atom_to_list(OtherAllocator)} + {skipped, + "Only run when glibc is used. " + "Allocator used: " + ++ atom_to_list(OtherAllocator)} end, - ?line test_server:timetrap_cancel(Dog), Result. - + run_wilderness_test({Set_tt, Set_tp}, {Exp_tt, Exp_tp}) -> Self = self(), Ref = make_ref(), SuiteDir = filename:dirname(code:which(?MODULE)), - ?line {ok, Node} = test_server:start_node(allocator_test, - slave, - [{args, - " -pa " - ++ SuiteDir - ++" +MYtt "++to_string(Set_tt) - ++" +MYtp "++to_string(Set_tp)}, - {linked, false}]), + {ok, Node} = test_server:start_node(allocator_test, + slave, + [{args, + " -pa " + ++ SuiteDir + ++" +MYtt "++to_string(Set_tt) + ++" +MYtp "++to_string(Set_tp)}, + {linked, false}]), spawn(Node, fun () -> Self ! {Ref, erlang:system_info(allocator)} end), @@ -560,15 +534,15 @@ run_wilderness_test({Set_tt, Set_tp}, {Exp_tt, Exp_tp}) -> {Ref, {A, V, F, S}} -> Ett = Exp_tt*1024, Etp = Exp_tp*1024, - ?line test_server:format("Test allocator info:~n~p", - [{A, V, F, S}]), - ?line {value, {sys_alloc, SA_Opts}} + io:format("Test allocator info:~n~p", + [{A, V, F, S}]), + {value, {sys_alloc, SA_Opts}} = lists:keysearch(sys_alloc, 1, S), - ?line {value, {tt, Ett}} = lists:keysearch(tt, 1, SA_Opts), - ?line {value, {tp, Etp}} = lists:keysearch(tp, 1, SA_Opts) + {value, {tt, Ett}} = lists:keysearch(tt, 1, SA_Opts), + {value, {tp, Etp}} = lists:keysearch(tp, 1, SA_Opts) end, stop_node(Node). - + to_string(X) when is_integer(X) -> integer_to_list(X); to_string(X) when is_atom(X) -> @@ -594,12 +568,12 @@ get_nodenames(N, T, Acc) -> ++ integer_to_list(C)) | Acc]). start_node(TestCase) -> - ?line [Name] = get_nodenames(1, TestCase), - ?line Pa = filename:dirname(code:which(?MODULE)), - ?line test_server:start_node(Name, slave, [{args, "-pa " ++ Pa}]). + [Name] = get_nodenames(1, TestCase), + Pa = filename:dirname(code:which(?MODULE)), + test_server:start_node(Name, slave, [{args, "-pa " ++ Pa}]). stop_node(Node) -> - ?line true = test_server:stop_node(Node). + true = test_server:stop_node(Node). run_fun(Fun) -> Fun(). diff --git a/lib/kernel/test/ch.erl b/lib/kernel/test/ch.erl index 25d6f6d200..c9f378e387 100644 --- a/lib/kernel/test/ch.erl +++ b/lib/kernel/test/ch.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2010. All Rights Reserved. +%% Copyright Ericsson AB 1996-2016. 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/lib/kernel/test/ch_sup.erl b/lib/kernel/test/ch_sup.erl index 4c923b2909..1d7f17fc95 100644 --- a/lib/kernel/test/ch_sup.erl +++ b/lib/kernel/test/ch_sup.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2010. All Rights Reserved. +%% Copyright Ericsson AB 1996-2016. 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/lib/kernel/test/cleanup.erl b/lib/kernel/test/cleanup.erl index 01db1e9124..db738c167e 100644 --- a/lib/kernel/test/cleanup.erl +++ b/lib/kernel/test/cleanup.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2010. All Rights Reserved. +%% Copyright Ericsson AB 1997-2016. 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/. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at %% -%% 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. +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% @@ -20,7 +21,7 @@ -export([all/0,groups/0,init_per_group/2,end_per_group/2, cleanup/1]). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). all() -> [cleanup]. @@ -35,15 +36,14 @@ end_per_group(_GroupName, Config) -> Config. -cleanup(suite) -> []; cleanup(_) -> - ?line Localhost = list_to_atom(net_adm:localhost()), - ?line net_adm:world_list([Localhost]), - ?line case nodes() of - [] -> - ok; - Nodes when is_list(Nodes) -> - Kill = fun(Node) -> spawn(Node, erlang, halt, []) end, - ?line lists:foreach(Kill, Nodes), - ?line test_server:fail({nodes_left, Nodes}) - end. + Localhost = list_to_atom(net_adm:localhost()), + net_adm:world_list([Localhost]), + case nodes() of + [] -> + ok; + Nodes when is_list(Nodes) -> + Kill = fun(Node) -> spawn(Node, erlang, halt, []) end, + lists:foreach(Kill, Nodes), + ct:fail({nodes_left, Nodes}) + end. diff --git a/lib/kernel/test/code_SUITE.erl b/lib/kernel/test/code_SUITE.erl index afedc17e57..1314316c13 100644 --- a/lib/kernel/test/code_SUITE.erl +++ b/lib/kernel/test/code_SUITE.erl @@ -1,24 +1,26 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2013. All Rights Reserved. +%% Copyright Ericsson AB 1996-2018. 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/. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at %% -%% 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. +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% -module(code_SUITE). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). +-include_lib("syntax_tools/include/merl.hrl"). -export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2]). -export([set_path/1, get_path/1, add_path/1, add_paths/1, del_path/1, @@ -29,12 +31,17 @@ upgrade/1, sticky_dir/1, pa_pz_option/1, add_del_path/1, dir_disappeared/1, ext_mod_dep/1, clash/1, - load_cached/1, start_node_with_cache/1, add_and_rehash/1, - where_is_file_cached/1, where_is_file_no_cache/1, + where_is_file/1, purge_stacktrace/1, mult_lib_roots/1, bad_erl_libs/1, code_archive/1, code_archive2/1, on_load/1, on_load_binary/1, - on_load_embedded/1, on_load_errors/1, big_boot_embedded/1, - native_early_modules/1, get_mode/1]). + on_load_embedded/1, on_load_errors/1, on_load_update/1, + on_load_trace_on_load/1, + on_load_purge/1, on_load_self_call/1, on_load_pending/1, + on_load_deleted/1, + big_boot_embedded/1, + module_status/1, + native_early_modules/1, get_mode/1, + normalized_paths/1]). -export([init_per_testcase/2, end_per_testcase/2, init_per_suite/1, end_per_suite/1]). @@ -46,7 +53,9 @@ -export([compile_load/4]). -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap,{minutes,5}}]. all() -> [set_path, get_path, add_path, add_paths, del_path, @@ -55,15 +64,19 @@ all() -> load_binary, dir_req, object_code, set_path_file, upgrade, sticky_dir, pa_pz_option, add_del_path, dir_disappeared, - ext_mod_dep, clash, load_cached, start_node_with_cache, - add_and_rehash, where_is_file_no_cache, - where_is_file_cached, purge_stacktrace, mult_lib_roots, + ext_mod_dep, clash, where_is_file, + purge_stacktrace, mult_lib_roots, bad_erl_libs, code_archive, code_archive2, on_load, on_load_binary, on_load_embedded, on_load_errors, - big_boot_embedded, native_early_modules, get_mode]. + {group, sequence}, + on_load_purge, on_load_self_call, on_load_pending, + on_load_deleted, + module_status, + big_boot_embedded, native_early_modules, get_mode, normalized_paths]. -groups() -> - []. +%% These need to run in order +groups() -> [{sequence, [sequence], [on_load_update, + on_load_trace_on_load]}]. init_per_group(_GroupName, Config) -> Config. @@ -85,6 +98,11 @@ init_per_suite(Config) -> end_per_suite(Config) -> Config. +-define(TESTMOD, test_dummy). +-define(TESTMODSTR, "test_dummy"). +-define(TESTMODSRC, ?TESTMODSTR ".erl"). +-define(TESTMODOBJ, ?TESTMODSTR ".beam"). + init_per_testcase(big_boot_embedded, Config) -> case catch crypto:start() of ok -> @@ -92,35 +110,49 @@ init_per_testcase(big_boot_embedded, Config) -> _Else -> {skip, "Needs crypto!"} end; +init_per_testcase(on_load_embedded, Config0) -> + LibRoot = code:lib_dir(), + LinkName = filename:join(LibRoot, "on_load_app-1.0"), + Config = [{link_name,LinkName}|Config0], + init_per_testcase(Config); init_per_testcase(_Func, Config) -> - Dog=?t:timetrap(?t:minutes(5)), - P=code:get_path(), - P=code:get_path(), - [{watchdog, Dog}, {code_path, P}|Config]. + init_per_testcase(Config). + +init_per_testcase(Config) -> + P = code:get_path(), + [{code_path, P}|Config]. + +end_per_testcase(module_status, Config) -> + code:purge(?TESTMOD), + code:delete(?TESTMOD), + code:purge(?TESTMOD), + file:delete(?TESTMODOBJ), + file:delete(?TESTMODSRC), + end_per_testcase(Config); end_per_testcase(TC, Config) when TC == mult_lib_roots; TC == big_boot_embedded -> {ok, HostName} = inet:gethostname(), NodeName = list_to_atom(atom_to_list(TC)++"@"++HostName), - ?t:stop_node(NodeName), + test_server:stop_node(NodeName), + end_per_testcase(Config); +end_per_testcase(on_load_embedded, Config) -> + LinkName = proplists:get_value(link_name, Config), + _ = del_link(LinkName), end_per_testcase(Config); end_per_testcase(_Func, Config) -> end_per_testcase(Config). end_per_testcase(Config) -> code:purge(code_b_test), - Dog=?config(watchdog, Config), - ?t:timetrap_cancel(Dog), - P=?config(code_path, Config), + P=proplists:get_value(code_path, Config), true=code:set_path(P), P=code:get_path(), ok. -set_path(suite) -> []; -set_path(doc) -> []; set_path(Config) when is_list(Config) -> P = code:get_path(), - NonExDir = filename:join(?config(priv_dir, Config), ?t:temp_name("hej")), + NonExDir = filename:join(proplists:get_value(priv_dir, Config), test_server:temp_name("hej")), {'EXIT',_} = (catch code:set_path({a})), {error, bad_directory} = (catch code:set_path([{a}])), {error, bad_directory} = code:set_path(NonExDir), @@ -134,19 +166,15 @@ set_path(Config) when is_list(Config) -> [LibDir] = code:get_path(), ok. -get_path(suite) -> []; -get_path(doc) -> []; get_path(Config) when is_list(Config) -> P = code:get_path(), - % test that all directories are strings (lists). + %% test that all directories are strings (lists). [] = lists:filter(fun (Dir) when is_list(Dir) -> false; (_) -> true end, P), ok. -add_path(suite) -> []; -add_path(doc) -> []; add_path(Config) when is_list(Config) -> P = code:get_path(), {'EXIT',_} = (catch code:add_path({})), @@ -167,8 +195,6 @@ add_path(Config) when is_list(Config) -> code:set_path(P), ok. -add_paths(suite) -> []; -add_paths(doc) -> []; add_paths(Config) when is_list(Config) -> P = code:get_path(), ok = code:add_paths([{}]), @@ -214,8 +240,6 @@ add_paths(Config) when is_list(Config) -> code:set_path(P), ok. -del_path(suite) -> []; -del_path(doc) -> []; del_path(Config) when is_list(Config) -> P = code:get_path(), try @@ -225,18 +249,18 @@ del_path(Config) when is_list(Config) -> end. del_path_1(P) -> - test_server:format("Initial code:get_path()=~p~n",[P]), + io:format("Initial code:get_path()=~p~n",[P]), {'EXIT',_} = (catch code:del_path(3)), false = code:del_path(my_dummy_name), false = code:del_path("/kdlk/my_dummy_dir"), Dir = filename:join([code:lib_dir(kernel),"ebin"]), - test_server:format("kernel dir: ~p~n",[Dir]), + io:format("kernel dir: ~p~n",[Dir]), true = code:del_path(kernel), NewP = code:get_path(), - test_server:format("Path after removing 'kernel':~p~n",[NewP]), + io:format("Path after removing 'kernel':~p~n",[NewP]), ReferenceP = lists:delete(Dir,P), - test_server:format("Reference path:~p~n",[ReferenceP]), + io:format("Reference path:~p~n",[ReferenceP]), NewP = ReferenceP, % check that dir is deleted code:set_path(P), @@ -250,10 +274,8 @@ del_path_1(P) -> NewP1 = lists:delete(Dir,P), % check that dir is deleted ok. -replace_path(suite) -> []; -replace_path(doc) -> []; replace_path(Config) when is_list(Config) -> - PrivDir = ?config(priv_dir, Config), + PrivDir = proplists:get_value(priv_dir, Config), P = code:get_path(), {'EXIT',_} = (catch code:replace_path(3,"")), {error, bad_name} = code:replace_path(dummy_name,""), @@ -288,10 +310,9 @@ replace_path(Config) when is_list(Config) -> ok. -dir_disappeared(suite) -> []; -dir_disappeared(doc) -> ["OTP-3977"]; +%% OTP-3977. dir_disappeared(Config) when is_list(Config) -> - PrivDir = ?config(priv_dir, Config), + PrivDir = proplists:get_value(priv_dir, Config), Dir = filename:join(PrivDir, "temp"), ok = file:make_dir(Dir), true = code:add_path(Dir), @@ -299,8 +320,6 @@ dir_disappeared(Config) when is_list(Config) -> non_existing = code:which(bubbelskrammel), ok. -load_file(suite) -> []; -load_file(doc) -> []; load_file(Config) when is_list(Config) -> {error, nofile} = code:load_file(duuuumy_mod), {error, badfile} = code:load_file(code_a_test), @@ -315,21 +334,18 @@ load_file(Config) when is_list(Config) -> test_dir() -> filename:dirname(code:which(?MODULE)). -load_abs(suite) -> []; -load_abs(doc) -> []; load_abs(Config) when is_list(Config) -> TestDir = test_dir(), {error, nofile} = code:load_abs(TestDir ++ "/duuuumy_mod"), {error, badfile} = code:load_abs(TestDir ++ "/code_a_test"), {'EXIT', _} = (catch code:load_abs({})), + {error, nofile} = code:load_abs("Non-latin-имя-файла"), {module, code_b_test} = code:load_abs(TestDir ++ "/code_b_test"), code:stick_dir(TestDir), {error, sticky_directory} = code:load_abs(TestDir ++ "/code_b_test"), code:unstick_dir(TestDir), ok. -ensure_loaded(suite) -> []; -ensure_loaded(doc) -> []; ensure_loaded(Config) when is_list(Config) -> {module, lists} = code:ensure_loaded(lists), case init:get_argument(mode) of @@ -346,8 +362,6 @@ ensure_loaded(Config) when is_list(Config) -> ok end. -delete(suite) -> []; -delete(doc) -> []; delete(Config) when is_list(Config) -> OldFlag = process_flag(trap_exit, true), code:purge(code_b_test), @@ -364,8 +378,6 @@ delete(Config) when is_list(Config) -> process_flag(trap_exit, OldFlag), ok. -purge(suite) -> []; -purge(doc) -> []; purge(Config) when is_list(Config) -> OldFlag = process_flag(trap_exit, true), code:purge(code_b_test), @@ -381,8 +393,23 @@ purge(Config) when is_list(Config) -> purge_many_exits(Config) when is_list(Config) -> OldFlag = process_flag(trap_exit, true), + code:purge(code_b_test), {'EXIT',_} = (catch code:purge({})), + + CodePurgeF = fun(M, Exp) -> Exp = code:purge(M) end, + purge_many_exits_do(CodePurgeF), + + %% Let's repeat test for erlang:purge_module as it does the same thing + %% now in erts-8.0 (except for return value). + ErlangPurgeF = fun(M, _Exp) -> erlang:purge_module(M) end, + purge_many_exits_do(ErlangPurgeF), + + process_flag(trap_exit, OldFlag), + ok. + + +purge_many_exits_do(PurgeF) -> false = code:purge(code_b_test), TPids = lists:map(fun (_) -> {code_b_test:do_spawn(), @@ -393,7 +420,7 @@ purge_many_exits(Config) when is_list(Config) -> end)} end, lists:seq(1, 1000)), - % Give them time to start... + %% Give them time to start... receive after 1000 -> ok end, true = code:delete(code_b_test), lists:foreach(fun ({Pid1, Pid2}) -> @@ -401,7 +428,7 @@ purge_many_exits(Config) when is_list(Config) -> false = code_b_test:check_exit(Pid1), true = erlang:is_process_alive(Pid2) end, TPids), - true = code:purge(code_b_test), + PurgeF(code_b_test, true), lists:foreach(fun ({Pid1, Pid2}) -> false = erlang:is_process_alive(Pid1), true = code_b_test:check_exit(Pid1), @@ -410,13 +437,9 @@ purge_many_exits(Config) when is_list(Config) -> end, TPids), lists:foreach(fun ({_Pid1, Pid2}) -> receive {'EXIT', Pid2, _} -> ok end - end, TPids), - process_flag(trap_exit, OldFlag), - ok. + end, TPids). -soft_purge(suite) -> []; -soft_purge(doc) -> []; soft_purge(Config) when is_list(Config) -> OldFlag = process_flag(trap_exit, true), code:purge(code_b_test), @@ -433,8 +456,6 @@ soft_purge(Config) when is_list(Config) -> process_flag(trap_exit, OldFlag), ok. -is_loaded(suite) -> []; -is_loaded(doc) -> []; is_loaded(Config) when is_list(Config) -> code:purge(code_b_test), code:delete(code_b_test), @@ -448,10 +469,8 @@ is_loaded(Config) when is_list(Config) -> code:delete(code_b_test), ok. -all_loaded(suite) -> []; -all_loaded(doc) -> []; all_loaded(Config) when is_list(Config) -> - case ?t:is_cover() of + case test_server:is_cover() of true -> {skip,"Cover is running"}; false -> all_loaded_1() end. @@ -479,8 +498,6 @@ all_unique([]) -> ok; all_unique([_]) -> ok; all_unique([{X,_}|[{Y,_}|_]=T]) when X < Y -> all_unique(T). -load_binary(suite) -> []; -load_binary(doc) -> []; load_binary(Config) when is_list(Config) -> TestDir = test_dir(), File = TestDir ++ "/code_b_test" ++ code:objfile_extension(), @@ -496,23 +513,35 @@ load_binary(Config) when is_list(Config) -> code:delete(code_b_test), ok. -upgrade(Config) -> - DataDir = ?config(data_dir, Config), - - %%T = [beam, hipe], - T = [beam], - [upgrade_do(DataDir, Client, U1, U2, O1, O2) - || Client<-T, U1<-T, U2<-T, O1<-T, O2<-T], +upgrade(Config) -> + DataDir = proplists:get_value(data_dir, Config), + case erlang:system_info(hipe_architecture) of + undefined -> + upgrade_do(DataDir, beam, [beam]); + + _ -> + T = [beam, hipe], + [upgrade_do(DataDir, Client, T) || Client <- T], + + case hipe:llvm_support_available() of + false -> ok; + true -> + T2 = [beam, hipe_llvm], + [upgrade_do(DataDir, Client, T2) || Client <- T2] + end + end, ok. -upgrade_do(DataDir, Client, U1, U2, O1, O2) -> +upgrade_do(DataDir, Client, T) -> compile_load(upgrade_client, DataDir, undefined, Client), - upgrade_client:run(DataDir, U1, U2, O1, O2), + [upgrade_client:run(DataDir, U1, U2, O1, O2) + || U1<-T, U2<-T, O1<-T, O2<-T], ok. compile_load(Mod, Dir, Ver, CodeType) -> + %%erlang:display({"{{{{{{{{{{{{{{{{Loading",Mod,Ver,CodeType}), Version = case Ver of undefined -> io:format("Compiling '~p' as ~p\n", [Mod, CodeType]), @@ -524,20 +553,23 @@ compile_load(Mod, Dir, Ver, CodeType) -> end, Target = case CodeType of beam -> []; - hipe -> [native] + hipe -> [native]; + hipe_llvm -> [native,{hipe,[to_llvm]}] end, CompOpts = [binary, report] ++ Target ++ Version, Src = filename:join(Dir, atom_to_list(Mod) ++ ".erl"), - %io:format("compile:file(~p,~p)\n", [Src, CompOpts]), + T1 = erlang:now(), {ok,Mod,Code} = compile:file(Src, CompOpts), + T2 = erlang:now(), ObjFile = filename:basename(Src,".erl") ++ ".beam", {module,Mod} = code:load_binary(Mod, ObjFile, Code), - %IsNative = code:is_module_native(Mod), + T3 = erlang:now(), + io:format("Compile time ~p ms, Load time ~p ms\n", + [timer:now_diff(T2,T1) div 1000, timer:now_diff(T3,T2) div 1000]), + %%erlang:display({"}}}}}}}}}}}}}}}Loaded",Mod,Ver,CodeType}), ok. -dir_req(suite) -> []; -dir_req(doc) -> []; dir_req(Config) when is_list(Config) -> {ok,[[Root0]]} = init:get_argument(root), Root = filename:join([Root0]), % Normalised form. @@ -552,8 +584,6 @@ dir_req(Config) when is_list(Config) -> {error, bad_name} = code:priv_dir(duuumy), ok. -object_code(suite) -> []; -object_code(doc) -> []; object_code(Config) when is_list(Config) -> TestDir = test_dir(), P = code:get_path(), @@ -574,22 +604,20 @@ object_code(Config) when is_list(Config) -> P=code:get_path(), ok. -set_path_file(suite) -> []; -set_path_file(doc) -> ["Test that set_path does not accept ", - "files as pathnames (known previous bug)"]; +%% Test that set_path does not accept +%% files as pathnames (known previous bug) set_path_file(Config) when is_list(Config) -> - File=filename:join(?config(priv_dir, Config), "testfil"), + File=filename:join(proplists:get_value(priv_dir, Config), "testfil"), ok=file:write_file(File, list_to_binary("lite data")), {error, bad_directory}=code:set_path([File]). -sticky_dir(suite) -> []; -sticky_dir(doc) -> ["Test that a module with the same name as a module in ", - "a sticky directory cannot be loaded."]; +%% Test that a module with the same name as a module in +%% a sticky directory cannot be loaded. sticky_dir(Config) when is_list(Config) -> Pa = filename:dirname(code:which(?MODULE)), - {ok,Node} = ?t:start_node(sticky_dir, slave, [{args,"-pa "++Pa}]), + {ok,Node} = test_server:start_node(sticky_dir, slave, [{args,"-pa "++Pa}]), Mods = [code,lists,erlang,init], - OutDir = filename:join(?config(priv_dir, Config), sticky_dir), + OutDir = filename:join(proplists:get_value(priv_dir, Config), sticky_dir), _ = file:make_dir(OutDir), Ret = rpc:call(Node, erlang, apply, [fun sticky_compiler/2,[Mods,OutDir]]), @@ -598,9 +626,9 @@ sticky_dir(Config) when is_list(Config) -> ok; Other -> io:format("~p\n", [Other]), - ?t:fail() + ct:fail(failed) end, - ?t:stop_node(Node), + test_server:stop_node(Node), ok. sticky_compiler(Files, PrivDir) -> @@ -609,50 +637,55 @@ sticky_compiler(Files, PrivDir) -> [R || R <- Rets, R =/= ok]. do_sticky_compile(Mod, Dir) -> - %% Make sure that the module is loaded. A module being sticky - %% only prevents it from begin reloaded, not from being loaded - %% from the wrong place to begin with. - Mod = Mod:module_info(module), - File = filename:append(Dir, atom_to_list(Mod)), - Src = io_lib:format("-module(~s).\n" - "-export([test/1]).\n" - "test(me) -> fail.\n", [Mod]), - ok = file:write_file(File++".erl", Src), - case c:c(File, [{outdir,Dir}]) of - {ok,Module} -> - Module:test(me); - {error,sticky_directory} -> - ok + case code:is_sticky(Mod) of + true -> + %% Make sure that the module is loaded. A module being sticky + %% only prevents it from begin reloaded, not from being loaded + %% from the wrong place to begin with. + Mod = Mod:module_info(module), + File = filename:append(Dir, atom_to_list(Mod)), + Src = io_lib:format("-module(~s).\n" + "-export([test/1]).\n" + "test(me) -> fail.\n", [Mod]), + ok = file:write_file(File++".erl", Src), + case c:c(File, [{outdir,Dir}]) of + {ok,Module} -> + Module:test(me); + {error,sticky_directory} -> + ok + end; + false -> + %% For some reason the module is not sticky + %% could be that the .erlang file has + %% unstuck it? + {Mod, is_not_sticky} end. -pa_pz_option(suite) -> []; -pa_pz_option(doc) -> ["Test that the -pa and -pz options work as expected"]; +%% Test that the -pa and -pz options work as expected. pa_pz_option(Config) when is_list(Config) -> - DDir = ?config(data_dir,Config), + DDir = proplists:get_value(data_dir,Config), PaDir = filename:join(DDir,"pa"), PzDir = filename:join(DDir,"pz"), - {ok, Node}=?t:start_node(pa_pz1, slave, + {ok, Node}=test_server:start_node(pa_pz1, slave, [{args, "-pa " ++ PaDir ++ " -pz " ++ PzDir}]), Ret=rpc:call(Node, code, get_path, []), [PaDir|Paths] = Ret, [PzDir|_] = lists:reverse(Paths), - ?t:stop_node(Node), - {ok, Node2}=?t:start_node(pa_pz2, slave, + test_server:stop_node(Node), + {ok, Node2}=test_server:start_node(pa_pz2, slave, [{args, "-mode embedded " ++ "-pa " ++ PaDir ++ " -pz " ++ PzDir}]), Ret2=rpc:call(Node2, code, get_path, []), [PaDir|Paths2] = Ret2, [PzDir|_] = lists:reverse(Paths2), - ?t:stop_node(Node2). + test_server:stop_node(Node2). -add_del_path(suite) -> - []; -add_del_path(doc) -> ["add_path, del_path should not cause priv_dir(App) to fail"]; +%% add_path, del_path should not cause priv_dir(App) to fail. add_del_path(Config) when is_list(Config) -> - DDir = ?config(data_dir,Config), + DDir = proplists:get_value(data_dir,Config), Dir1 = filename:join(DDir,"dummy_app-1.0/ebin"), Dir2 = filename:join(DDir,"dummy_app-2.0/ebin"), code:add_patha(Dir1), @@ -666,43 +699,35 @@ add_del_path(Config) when is_list(Config) -> clash(Config) when is_list(Config) -> - DDir = ?config(data_dir,Config)++"clash/", + DDir = proplists:get_value(data_dir,Config)++"clash/", P = code:get_path(), - [TestServerPath|_] = [Path || Path <- code:get_path(), - re:run(Path,"test_server/?$",[unicode]) /= nomatch], %% test non-clashing entries - %% remove TestServerPath to prevent clash with test-server path - true = code:del_path(TestServerPath), true = code:add_path(DDir++"foobar-0.1/ebin"), true = code:add_path(DDir++"zork-0.8/ebin"), - test_server:capture_start(), + ct:capture_start(), ok = code:clash(), - test_server:capture_stop(), - [OKMsg|_] = test_server:capture_get(), + ct:capture_stop(), + [OKMsg|_] = ct:capture_get(), true = lists:prefix("** Found 0 name clashes", OKMsg), true = code:set_path(P), %% test clashing entries - %% remove TestServerPath to prevent clash with test-server path - true = code:del_path(TestServerPath), true = code:add_path(DDir++"foobar-0.1/ebin"), true = code:add_path(DDir++"foobar-0.1.ez/foobar-0.1/ebin"), - test_server:capture_start(), + ct:capture_start(), ok = code:clash(), - test_server:capture_stop(), - [ClashMsg|_] = test_server:capture_get(), + ct:capture_stop(), + [ClashMsg|_] = ct:capture_get(), {match, [" hides "]} = re:run(ClashMsg, "\\*\\* .*( hides ).*", [{capture,all_but_first,list}]), true = code:set_path(P), %% test "Bad path can't read" - %% remove TestServerPath to prevent clash with test-server path - Priv = ?config(priv_dir, Config), - true = code:del_path(TestServerPath), + Priv = proplists:get_value(priv_dir, Config), TmpEzFile = Priv++"foobar-0.tmp.ez", {ok, _} = file:copy(DDir++"foobar-0.1.ez", TmpEzFile), true = code:add_path(TmpEzFile++"/foobar-0.1/ebin"), @@ -714,20 +739,17 @@ clash(Config) when is_list(Config) -> _ -> ok = file:delete(TmpEzFile) end, - test_server:capture_start(), + ct:capture_start(), ok = code:clash(), - test_server:capture_stop(), - [BadPathMsg|_] = test_server:capture_get(), + ct:capture_stop(), + [BadPathMsg|_] = ct:capture_get(), true = lists:prefix("** Bad path can't read", BadPathMsg), true = code:set_path(P), file:delete(TmpEzFile++".moved"), %% Only effect on windows ok. -ext_mod_dep(suite) -> - []; -ext_mod_dep(doc) -> - ["Every module that the code_server uses should be preloaded, " - "this test case verifies that"]; +%% Every module that the code_server uses should be preloaded, +%% this test case verifies that. ext_mod_dep(Config) when is_list(Config) -> xref:start(s), xref:set_default(s, [{verbose,false},{warnings,false}, @@ -743,7 +765,7 @@ ext_mod_dep(Config) when is_list(Config) -> xref:stop(s), case Else of ok -> ok; - _ -> test_server:fail(Else) + _ -> ct:fail(Else) end end. @@ -767,6 +789,7 @@ analyse([], [This={M,F,A}|Path], Visited, ErrCnt0) -> OK = [erlang, os, prim_file, erl_prim_loader, init, ets, code_server, lists, lists_sort, unicode, binary, filename, gb_sets, gb_trees, hipe_unified_loader, hipe_bifs, + erts_code_purger, prim_zip, zlib], ErrCnt1 = case lists:member(M, OK) or erlang:is_builtin(M,F,A) of @@ -792,7 +815,7 @@ analyse2(MFA={_,_,_}, Path, Visited0) -> analyse(FL, [MFA|Path], my_usort([MFA|Visited0]), 0). %%%% We need to check these manually... -% fun's are ok as long as they are defined locally. +%% fun's are ok as long as they are defined locally. check_funs({'$M_EXPR','$F_EXPR',_}, [{unicode,characters_to_binary_int,3}, {unicode,characters_to_binary,3}, @@ -810,14 +833,6 @@ check_funs({'$M_EXPR','$F_EXPR',_}, {unicode,characters_to_binary,3}, {filename,filename_string_to_binary,1}|_]) -> 0; check_funs({'$M_EXPR','$F_EXPR',_}, - [{code_server,load_native_code,4}, - {code_server,load_native_code_1,2}, - {code_server,load_native_code,2}, - {code_server,try_load_module,4}, - {code_server,do_load_binary,4}, - {code_server,handle_call,3}, - {code_server,loop,1}|_]) -> 0; -check_funs({'$M_EXPR','$F_EXPR',_}, [{code_server,do_mod_call,4}, {code_server,handle_call,3}|_]) -> 0; check_funs({'$M_EXPR','$F_EXPR',_}, @@ -845,51 +860,48 @@ check_funs({'$M_EXPR','$F_EXPR',1}, {code_server,start_link,1}]) -> 0; check_funs({'$M_EXPR','$F_EXPR',1}, [{lists,filter,2}, - {code_server,try_archive_subdirs,3}, - {code_server,all_archive_subdirs,1}, - {code_server,archive_subdirs,1}, - {code_server,insert_name,3}, - {code_server,replace_name,2}, - {code_server,update,2}, - {code_server,maybe_update,2}, - {code_server,do_add,4}, - {code_server,add_path,4}, - {code_server,handle_call,3}, - {code_server,loop,1}, - {code_server,system_continue,3}]) -> 0; + {code_server,try_archive_subdirs,3}|_]) -> 0; check_funs({'$M_EXPR','$F_EXPR',_}, [{erlang,apply,2}, {erlang,spawn_link,1}, {code_server,start_link,1}]) -> 0; check_funs({'$M_EXPR','$F_EXPR',_}, [{erlang,spawn_link,1},{code_server,start_link,1}]) -> 0; -check_funs({'$M_EXPR',module_info,1}, - [{hipe_unified_loader,patch_to_emu_step1,1} | _]) -> 0; +check_funs({'$M_EXPR','$F_EXPR',2}, + [{hipe_unified_loader,write_words,3} | _]) -> 0; +check_funs({'$M_EXPR','$F_EXPR',2}, + [{hipe_unified_loader,patch_label_or_labels,4} | _]) -> 0; +check_funs({'$M_EXPR','$F_EXPR',2}, + [{hipe_unified_loader,sort_and_write,5} | _]) -> 0; check_funs({'$M_EXPR','$F_EXPR',2}, [{lists,foldl,3}, - {hipe_unified_loader,sort_and_write,4} | _]) -> 0; + {hipe_unified_loader,sort_and_write,5} | _]) -> 0; check_funs({'$M_EXPR','$F_EXPR',1}, [{lists,foreach,2}, - {hipe_unified_loader,patch_consts,3} | _]) -> 0; -check_funs({'$M_EXPR','$F_EXPR',1}, - [{lists,foreach,2}, - {hipe_unified_loader,mark_referred_from,1}, - {hipe_unified_loader,get_refs_from,2}| _]) -> 0; + {hipe_unified_loader,patch_consts,4} | _]) -> 0; check_funs({'$M_EXPR',warning_msg,2}, [{code_server,finish_on_load_report,2} | _]) -> 0; +check_funs({'$M_EXPR','$F_EXPR',1}, + [{code_server,run,2}|_]) -> 0; +check_funs({'$M_EXPR','$F_EXPR',2}, + [{code_server,handle_on_load,5}|_]) -> 0; +check_funs({'$M_EXPR','$F_EXPR',2}, + [{code_server,handle_pending_on_load,4}|_]) -> 0; +check_funs({'$M_EXPR','$F_EXPR',2}, + [{code_server,finish_on_load_2,3}|_]) -> 0; %% This is cheating! /raimo %% %% check_funs(This = {M,_,_}, Path) -> %% case catch atom_to_list(M) of %% [$h,$i,$p,$e | _] -> -%% test_server:format("hipe_module_ignored(~p, ~p)~n", [This, Path]), +%% io:format("hipe_module_ignored(~p, ~p)~n", [This, Path]), %% 0; %% _ -> -%% test_server:format("not_verified(~p, ~p)~n", [This, Path]), +%% io:format("not_verified(~p, ~p)~n", [This, Path]), %% 1 %% end; check_funs(This, Path) -> - test_server:format("not_verified(~p, ~p)~n", [This, Path]), + io:format("not_verified(~p, ~p)~n", [This, Path]), 1. my_usort(List) -> @@ -905,140 +917,7 @@ uniq([H|T],A) -> uniq(T,[H|A]). -load_cached(suite) -> - []; -load_cached(doc) -> - []; -load_cached(Config) when is_list(Config) -> - Priv = ?config(priv_dir, Config), - WD = filename:dirname(code:which(?MODULE)), - {ok,Node} = - ?t:start_node(code_cache_node, peer, [{args, - "-pa \"" ++ WD ++ "\""}, - {erl, [this]}]), - CCTabCreated = fun(Tab) -> - case ets:info(Tab, name) of - code_cache -> true; - _ -> false - end - end, - Tabs = rpc:call(Node, ets, all, []), - case rpc:call(Node, lists, any, [CCTabCreated,Tabs]) of - true -> - ?t:stop_node(Node), - ?t:fail("Code cache should not be active!"); - false -> - ok - end, - rpc:call(Node, code, del_path, [Priv]), - rpc:call(Node, code, add_pathz, [Priv]), - - FullModName = Priv ++ "/code_cache_test", - {ok,Dev} = file:open(FullModName ++ ".erl", [write]), - io:format(Dev, "-module(code_cache_test). -export([a/0]). a() -> ok.~n", []), - ok = file:close(Dev), - {ok,code_cache_test} = compile:file(FullModName, [{outdir,Priv}]), - - F = fun load_loop/2, - N = 1000, - {T0,T1} = rpc:call(Node, erlang, apply, [F, [N,code_cache_test]]), - TNoCache = now_diff(T1, T0), - rpc:call(Node, code, rehash, []), - {T2,T3} = rpc:call(Node, erlang, apply, [F, [N,code_cache_test]]), - TCache = now_diff(T3, T2), - AvgNoCache = TNoCache/N, - AvgCache = TCache/N, - io:format("Avg. load time (no_cache/cache): ~w/~w~n", [AvgNoCache,AvgCache]), - ?t:stop_node(Node), - if AvgNoCache =< AvgCache -> - ?t:fail("Cache not working properly."); - true -> - ok - end. - -load_loop(N, M) -> - load_loop(N, M, now()). -load_loop(0, _M, T0) -> - {T0,now()}; -load_loop(N, M, T0) -> - code:load_file(M), - code:delete(M), - code:purge(M), - load_loop(N-1, M, T0). - -now_diff({A2, B2, C2}, {A1, B1, C1}) -> - ((A2-A1)*1000000 + B2-B1)*1000000 + C2-C1. - -start_node_with_cache(suite) -> - []; -start_node_with_cache(doc) -> - []; -start_node_with_cache(Config) when is_list(Config) -> - {ok,Node} = - ?t:start_node(code_cache_node, peer, [{args, - "-code_path_cache"}, - {erl, [this]}]), - Tabs = rpc:call(Node, ets, all, []), - io:format("Tabs: ~w~n", [Tabs]), - CCTabCreated = fun(Tab) -> - case rpc:call(Node, ets, info, [Tab,name]) of - code_cache -> true; - _ -> false - end - end, - true = lists:any(CCTabCreated, Tabs), - ?t:stop_node(Node), - ok. - -add_and_rehash(suite) -> - []; -add_and_rehash(doc) -> - []; -add_and_rehash(Config) when is_list(Config) -> - Priv = ?config(priv_dir, Config), - WD = filename:dirname(code:which(?MODULE)), - {ok,Node} = - ?t:start_node(code_cache_node, peer, [{args, - "-pa \"" ++ WD ++ "\""}, - {erl, [this]}]), - CCTabCreated = fun(Tab) -> - case ets:info(Tab, name) of - code_cache -> true; - _ -> false - end - end, - Tabs0 = rpc:call(Node, ets, all, []), - case rpc:call(Node, lists, any, [CCTabCreated,Tabs0]) of - true -> - ?t:stop_node(Node), - ?t:fail("Code cache should not be active!"); - false -> - ok - end, - ok = rpc:call(Node, code, rehash, []), % create cache - Tabs1 = rpc:call(Node, ets, all, []), - true = rpc:call(Node, lists, any, [CCTabCreated,Tabs1]), % cache table created - ok = rpc:call(Node, code, rehash, []), - OkDir = filename:join(Priv, ""), - BadDir = filename:join(Priv, "guggemuffsussiputt"), - CP = [OkDir | rpc:call(Node, code, get_path, [])], - true = rpc:call(Node, code, set_path, [CP]), - CP1 = [BadDir | CP], - {error,_} = rpc:call(Node, code, set_path, [CP1]), - true = rpc:call(Node, code, del_path, [OkDir]), - true = rpc:call(Node, code, add_path, [OkDir]), - true = rpc:call(Node, code, add_path, [OkDir]), - {error,_} = rpc:call(Node, code, add_path, [BadDir]), - ok = rpc:call(Node, code, rehash, []), - - ?t:stop_node(Node), - ok. - -where_is_file_no_cache(suite) -> - []; -where_is_file_no_cache(doc) -> - []; -where_is_file_no_cache(Config) when is_list(Config) -> +where_is_file(Config) when is_list(Config) -> {T,KernelBeamFile} = timer:tc(code, where_is_file, ["kernel.beam"]), io:format("Load time: ~w ms~n", [T]), KernelEbinDir = filename:dirname(KernelBeamFile), @@ -1047,80 +926,45 @@ where_is_file_no_cache(Config) when is_list(Config) -> non_existing = code:where_is_file("kernel"), % no such file ok. -where_is_file_cached(suite) -> - []; -where_is_file_cached(doc) -> - []; -where_is_file_cached(Config) when is_list(Config) -> - {ok,Node} = - ?t:start_node(code_cache_node, peer, [{args, - "-code_path_cache"}, - {erl, [this]}]), - Tabs = rpc:call(Node, ets, all, []), - io:format("Tabs: ~w~n", [Tabs]), - CCTabCreated = fun(Tab) -> - case rpc:call(Node, ets, info, [Tab,name]) of - code_cache -> true; - _ -> false - end - end, - true = lists:any(CCTabCreated, Tabs), - KernelBeamFile = rpc:call(Node, code, where_is_file, ["kernel.beam"]), - {T,KernelBeamFile} = rpc:call(Node, timer, tc, [code,where_is_file,["kernel.beam"]]), - io:format("Load time: ~w ms~n", [T]), - KernelEbinDir = rpc:call(Node, filename, dirname, [KernelBeamFile]), - AppFile = rpc:call(Node, filename, join, [KernelEbinDir,"kernel.app"]), - AppFile = rpc:call(Node, code, where_is_file, ["kernel.app"]), - non_existing = rpc:call(Node, code, where_is_file, ["kernel"]), % no such file - ?t:stop_node(Node), - ok. - - -purge_stacktrace(suite) -> - []; -purge_stacktrace(doc) -> - ["Test that stacktrace is deleted when purging a referred module"]; +%% Test that stacktrace is deleted when purging a referred module. purge_stacktrace(Config) when is_list(Config) -> code:purge(code_b_test), try code_b_test:call(fun(b) -> ok end, a) catch - error:function_clause -> + error:function_clause:Stacktrace -> code:load_file(code_b_test), - case erlang:get_stacktrace() of + case Stacktrace of [{?MODULE,_,[a],_}, {code_b_test,call,2,_}, {?MODULE,purge_stacktrace,1,_}|_] -> - false = code:purge(code_b_test), - [] = erlang:get_stacktrace() + false = code:purge(code_b_test) end end, try code_b_test:call(nofun, 2) catch - error:function_clause -> + error:function_clause:Stacktrace2 -> code:load_file(code_b_test), - case erlang:get_stacktrace() of + case Stacktrace2 of [{code_b_test,call,[nofun,2],_}, {?MODULE,purge_stacktrace,1,_}|_] -> - false = code:purge(code_b_test), - [] = erlang:get_stacktrace() + false = code:purge(code_b_test) end end, Args = [erlang,error,[badarg]], try code_b_test:call(erlang, error, [badarg,Args]) catch - error:badarg -> + error:badarg:Stacktrace3 -> code:load_file(code_b_test), - case erlang:get_stacktrace() of + case Stacktrace3 of [{code_b_test,call,Args,_}, {?MODULE,purge_stacktrace,1,_}|_] -> - false = code:purge(code_b_test), - [] = erlang:get_stacktrace() + false = code:purge(code_b_test) end end, ok. mult_lib_roots(Config) when is_list(Config) -> - DataDir = filename:join(?config(data_dir, Config), "mult_lib_roots"), + DataDir = filename:join(proplists:get_value(data_dir, Config), "mult_lib_roots"), mult_lib_compile(DataDir, "my_dummy_app-b/ebin/lists"), mult_lib_compile(DataDir, "my_dummy_app-c/ebin/code_SUITE_mult_root_module"), @@ -1130,12 +974,11 @@ mult_lib_roots(Config) when is_list(Config) -> filename:join(DataDir, "second_root"), {ok,Node} = - ?t:start_node(mult_lib_roots, slave, + test_server:start_node(mult_lib_roots, slave, [{args,"-env ERL_LIBS "++ErlLibs}]), - TSPath = filename:dirname(code:which(test_server)), Path0 = rpc:call(Node, code, get_path, []), - [TSPath,"."|Path1] = Path0, + ["."|Path1] = Path0, [Kernel|Path2] = Path1, [Stdlib|Path3] = Path2, mult_lib_verify_lib(Kernel, "kernel"), @@ -1179,16 +1022,19 @@ mult_lib_remove_prefix([$/|T], []) -> T. bad_erl_libs(Config) when is_list(Config) -> {ok,Node} = - ?t:start_node(mult_lib_roots, slave, - [{args,"-env ERL_LIBS "}]), - - ?t:stop_node(Node), + test_server:start_node(bad_erl_libs, slave, []), + Code = rpc:call(Node,code,get_path,[]), + test_server:stop_node(Node), {ok,Node2} = - ?t:start_node(mult_lib_roots, slave, - [{args,"-env ERL_LIBS /no/such/dir"}]), + test_server:start_node(bad_erl_libs, slave, + [{args,"-env ERL_LIBS /no/such/dir"}]), + Code2 = rpc:call(Node,code,get_path,[]), + test_server:stop_node(Node2), + + %% Test that code path is not affected by the faulty ERL_LIBS + Code = Code2, - ?t:stop_node(Node2), ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -1203,8 +1049,8 @@ code_archive2(Config) when is_list(Config) -> do_code_archive(Config, Root, StripVsn) when is_list(Config) -> %% Copy the orig files to priv_dir - DataDir = ?config(data_dir, Config), - PrivDir = ?config(priv_dir, Config), + DataDir = proplists:get_value(data_dir, Config), + PrivDir = proplists:get_value(priv_dir, Config), App = code_archive_dict, VsnBase = atom_to_list(App) ++ "-1.0", Base = @@ -1237,9 +1083,15 @@ do_code_archive(Config, Root, StripVsn) when is_list(Config) -> {ok, _} = zip:create(Archive, [Base], [{compress, []}, {cwd, PrivDir}]), + %% Create a directory and a file outside of the archive. + OtherFile = filename:join([RootDir,VsnBase,"other","other.txt"]), + OtherContents = ?MODULE:module_info(md5), + filelib:ensure_dir(OtherFile), + ok = file:write_file(OtherFile, OtherContents), + %% Set up ERL_LIBS and start a slave node. {ok, Node} = - ?t:start_node(code_archive, slave, + test_server:start_node(code_archive, slave, [{args,"-env ERL_LIBS " ++ RootDir}]), CodePath = rpc:call(Node, code, get_path, []), AppEbin = filename:join([Archive, Base, "ebin"]), @@ -1251,13 +1103,25 @@ do_code_archive(Config, Root, StripVsn) when is_list(Config) -> %% Start the app ok = rpc:call(Node, application, start, [App]), + %% Get the lib dir for the app. + AppLibDir = rpc:call(Node, code, lib_dir, [App]), + io:format("AppLibDir: ~p\n", [AppLibDir]), + AppLibDir = filename:join(RootDir, VsnBase), + %% Access the app priv dir AppPrivDir = rpc:call(Node, code, priv_dir, [App]), AppPrivFile = filename:join([AppPrivDir, "code_archive.txt"]), io:format("AppPrivFile: ~p\n", [AppPrivFile]), - {ok, _Bin, _Path} = + {ok, _Bin, _} = rpc:call(Node, erl_prim_loader, get_file, [AppPrivFile]), + %% Read back the other text file. + OtherDirPath = rpc:call(Node, code, lib_dir, [App,other]), + OtherFilePath = filename:join(OtherDirPath, "other.txt"), + io:format("OtherFilePath: ~p\n", [OtherFilePath]), + {ok, OtherContents, _} = + rpc:call(Node, erl_prim_loader, get_file, [OtherFilePath]), + %% Use the app Tab = code_archive_tab, Key = foo, @@ -1270,7 +1134,7 @@ do_code_archive(Config, Root, StripVsn) when is_list(Config) -> error = rpc:call(Node, App, find, [Tab, Key]), ok = rpc:call(Node, App, erase, [Tab]), - ?t:stop_node(Node), + test_server:stop_node(Node), ok. compile_app(TopDir, AppName) -> @@ -1296,15 +1160,12 @@ compile_files([File | Files], SrcDir, OutDir) -> compile_files([], _, _) -> ok. -big_boot_embedded(suite) -> - []; -big_boot_embedded(doc) -> - ["Test that a boot file with (almost) all of OTP can be used to start an" - " embeddedd system."]; +%% Test that a boot file with (almost) all of OTP can be used to start an +%% embeddedd system. big_boot_embedded(Config) when is_list(Config) -> {BootArg,AppsInBoot} = create_big_boot(Config), {ok, Node} = - ?t:start_node(big_boot_embedded, slave, + test_server:start_node(big_boot_embedded, slave, [{args,"-boot "++BootArg++" -mode embedded"}]), RemoteNodeApps = [ {X,Y} || {X,_,Y} <- @@ -1315,7 +1176,7 @@ big_boot_embedded(Config) when is_list(Config) -> on_load(Config) when is_list(Config) -> Master = on_load_test_case_process, - Data = filename:join([?config(data_dir, Config),"on_load"]), + Data = filename:join([proplists:get_value(data_dir, Config),"on_load"]), ok = file:set_cwd(Data), up_to_date = make:all([{d,'MASTER',Master}]), @@ -1359,7 +1220,7 @@ on_load(Config) when is_list(Config) -> on_load_wait_for_all(Refs), receive Any -> - ?t:fail({unexpected,Any}) + ct:fail({unexpected,Any}) after 10 -> ok end. @@ -1383,21 +1244,17 @@ on_load_binary(_) -> register(Master, self()), %% Construct, compile and pretty-print. - Mod = on_load_binary, + Mod = ?FUNCTION_NAME, File = atom_to_list(Mod) ++ ".erl", - Forms = [{attribute,1,file,{File,1}}, - {attribute,1,module,Mod}, - {attribute,2,export,[{ok,0}]}, - {attribute,3,on_load,{init,0}}, - {function,5,init,0, - [{clause,5,[],[], - [{op,6,'!', - {atom,6,Master}, - {tuple,6,[{atom,6,Mod},{call,6,{atom,6,self},[]}]}}, - {'receive',7,[{clause,8,[{atom,8,go}],[],[{atom,8,ok}]}]}]}]}, - {function,11,ok,0,[{clause,11,[],[],[{atom,11,true}]}]}], - {ok,Mod,Bin} = compile:forms(Forms, [report]), - [io:put_chars(erl_pp:form(F)) || F <- Forms], + Tree = ?Q(["-module('@Mod@').\n", + "-export([ok/0]).\n", + "-on_load({init,0}).\n", + "init() ->\n", + " '@Master@' ! {on_load_binary,self()},\n", + " receive go -> ok end.\n", + "ok() -> true.\n"]), + {ok,Mod,Bin} = merl:compile(Tree), + merl:print(Tree), {Pid1,Ref1} = spawn_monitor(fun() -> code:load_binary(Mod, File, Bin), @@ -1426,17 +1283,19 @@ on_load_embedded(Config) when is_list(Config) -> end. on_load_embedded_1(Config) -> - DataDir = ?config(data_dir, Config), + DataDir = proplists:get_value(data_dir, Config), + LinkName = proplists:get_value(link_name, Config), %% Link the on_load_app application into the lib directory. - LibRoot = code:lib_dir(), - LinkName = filename:join(LibRoot, "on_load_app-1.0"), OnLoadApp = filename:join(DataDir, "on_load_app-1.0"), del_link(LinkName), io:format("LinkName :~p, OnLoadApp: ~p~n",[LinkName,OnLoadApp]), case file:make_symlink(OnLoadApp, LinkName) of {error,enotsup} -> throw({skip,"Support for symlinks required"}); + {error,eperm} -> + %% On Windows, we may not have permissions to create symlinks. + throw({skip,"Support for symlinks required"}); ok -> ok end, @@ -1461,8 +1320,7 @@ on_load_embedded_1(Config) -> ok = rpc:call(Node, on_load_embedded, status, []), %% Clean up. - stop_node(Node), - ok = del_link(LinkName). + stop_node(Node). del_link(LinkName) -> case file:delete(LinkName) of @@ -1481,7 +1339,7 @@ create_boot(Config, Options) -> filename:join(LatestDir, LatestName). create_script(Config) -> - PrivDir = ?config(priv_dir, Config), + PrivDir = proplists:get_value(priv_dir, Config), Name = PrivDir ++ "on_load_test", Apps = application_controller:which_applications(), {value,{_,_,KernelVer}} = lists:keysearch(kernel, 1, Apps), @@ -1508,33 +1366,45 @@ create_big_boot(Config) -> ok = file:set_cwd(OldDir), {filename:join(LatestDir, LatestName),Apps}. -% The following apps cannot be loaded -% hipe .app references (or can reference) files that have no -% corresponding beam file (if hipe is not enabled) +%% The following apps cannot be loaded. +%% hipe .app references (or can reference) files that have no +%% corresponding beam file (if hipe is not enabled). filter_app("hipe",_) -> false; -% Dialyzer and typer depends on hipe + +%% Dialyzer depends on hipe filter_app("dialyzer",_) -> false; -filter_app("typer",_) -> false; -% Orber requires explicit configuration + +%% Orber requires explicit configuration filter_app("orber",_) -> false; -% cos* depends on orber + +%% cos* depends on orber filter_app("cos"++_,_) -> false; -% ic has a mod instruction in the app file but no corresponding start function + +%% ic has a mod instruction in the app file but no corresponding start +%% function filter_app("ic",_) -> false; -% Netconf has some dependency that I really do not understand (maybe like orber) + +%% Netconf has some dependency that I really do not understand (maybe +%% like orber) filter_app("netconf",_) -> false; -% Safe has the same kind of error in the .app file as ic + +%% Safe has the same kind of error in the .app file as ic filter_app("safe",_) -> false; -% Comte cannot be started in the "usual" way + +%% Comte cannot be started in the "usual" way filter_app("comte",_) -> false; -% OS_mon does not find it's port program when running cerl + +%% OS_mon does not find its port program when running cerl filter_app("os_mon",true) -> false; -% erts is not a "real" app either =/ + +%% erts is not a "real" app either =/ filter_app("erts",_) -> false; -% Other apps should be OK. + +%% Other apps should be OK. filter_app(_,_) -> true. + create_big_script(Config,Local) -> - PrivDir = ?config(priv_dir, Config), + PrivDir = proplists:get_value(priv_dir, Config), Name = filename:join(PrivDir,"full_script_test"), InitialApplications=application:loaded_applications(), %% Applications left loaded by the application suite, unload them! @@ -1568,7 +1438,7 @@ on_load_errors(Config) when is_list(Config) -> Master = on_load_error_test_case_process, register(Master, self()), - Data = filename:join([?config(data_dir, Config),"on_load_errors"]), + Data = filename:join([proplists:get_value(data_dir, Config),"on_load_errors"]), ok = file:set_cwd(Data), up_to_date = make:all([{d,'MASTER',Master}]), @@ -1594,11 +1464,22 @@ on_load_errors(Config) when is_list(Config) -> %% There should be no more messages. receive Unexpected -> - ?t:fail({unexpected,Unexpected}) + ct:fail({unexpected,Unexpected}) after 10 -> ok end, + %% Make sure that the code loading functions return the correct + %% error code. + Simple = simple_on_load_error, + SimpleList = atom_to_list(Simple), + {error,on_load_failure} = code:load_file(Simple), + {error,on_load_failure} = code:ensure_loaded(Simple), + {ok,SimpleCode} = file:read_file("simple_on_load_error.beam"), + {error,on_load_failure} = code:load_binary(Simple, "", SimpleCode), + {error,on_load_failure} = code:load_abs(SimpleList), + {error,on_load_failure} = code:load_abs(SimpleList, Simple), + ok. do_on_load_error(ReturnValue) -> @@ -1612,8 +1493,284 @@ do_on_load_error(ReturnValue) -> {undef,[{on_load_error,main,[],_}|_]} = Exit end. -native_early_modules(suite) -> []; -native_early_modules(doc) -> ["Test that the native code of early loaded modules is loaded"]; +on_load_update(Config) -> + {Mod,Code1} = on_load_update_code(1), + {module,Mod} = code:load_binary(Mod, "", Code1), + 42 = Mod:a(), + 100 = Mod:b(99), + 4 = erlang:trace_pattern({Mod,'_','_'}, true), + + {Mod,Code2} = on_load_update_code(2), + {error,on_load_failure} = code:load_binary(Mod, "", Code2), + 42 = Mod:a(), + 78 = Mod:b(77), + {'EXIT',{undef,_}} = (catch Mod:never()), + 4 = erlang:trace_pattern({Mod,'_','_'}, false), + + {Mod,Code3} = on_load_update_code(3), + {module,Mod} = code:load_binary(Mod, "", Code3), + 100 = Mod:c(), + {'EXIT',{undef,_}} = (catch Mod:a()), + {'EXIT',{undef,_}} = (catch Mod:b(10)), + {'EXIT',{undef,_}} = (catch Mod:never()), + + code:purge(Mod), + code:delete(Mod), + code:purge(Mod), + ok. + +on_load_update_code(Version) -> + Mod = ?FUNCTION_NAME, + Tree = on_load_update_code_1(Version, Mod), + io:format("Version ~p", [Version]), + {ok,Mod,Code} = merl:compile(Tree), + merl:print(Tree), + io:nl(), + {Mod,Code}. + +on_load_update_code_1(1, Mod) -> + ?Q(["-module('@Mod@').\n", + "-export([a/0,b/1]).\n" + "-on_load(f/0).\n", + "f() -> ok.\n", + "a() -> 42.\n" + "b(I) -> I+1.\n"]); +on_load_update_code_1(2, Mod) -> + ?Q(["-module('@Mod@').\n", + "-export([never/0]).\n" + "-on_load(f/0).\n", + "f() -> 42 = '@Mod@':a(), 1 = '@Mod@':b(0), fail.\n", + "never() -> never.\n"]); +on_load_update_code_1(3, Mod) -> + ?Q(["-module('@Mod@').\n", + "-export([c/0]).\n" + "-on_load(f/0).\n", + "f() -> ok.\n", + "c() -> 100.\n"]). + +%% Test -on_load while trace feature 'on_load' is enabled (OTP-14612) +on_load_trace_on_load(Config) -> + Papa = self(), + Tracer = spawn_link(fun F() -> receive M -> Papa ! M end, F() end), + {tracer,[]} = erlang:trace_info(self(),tracer), + erlang:trace(self(), true, [call, {tracer, Tracer}]), + erlang:trace_pattern(on_load, true, []), + on_load_update(Config), + erlang:trace_pattern(on_load, false, []), + erlang:trace(self(), false, [call]), + + Ms = flush(), + [{trace, Papa, call, {on_load_update_code, a, []}}, + {trace, Papa, call, {on_load_update_code, b, [99]}}, + {trace, Papa, call, {on_load_update_code, c, []}}] = Ms, + + exit(Tracer, normal), + ok. + +flush() -> + receive M -> [M | flush()] + after 100 -> [] + end. + + +on_load_purge(_Config) -> + Mod = ?FUNCTION_NAME, + register(Mod, self()), + Tree = ?Q(["-module('@Mod@').\n", + "-on_load(f/0).\n", + "loop() -> loop().\n", + "f() ->\n", + "'@Mod@' ! {self(),spawn(fun loop/0)},\n", + "receive Ack -> Ack end.\n"]), + merl:print(Tree), + {ok,Mod,Code} = merl:compile(Tree), + P = spawn(fun() -> + exit(code:load_binary(Mod, "", Code)) + end), + monitor(process, P), + receive + {Pid1,Pid2} -> + monitor(process, Pid2), + Pid1 ! ack_and_failure, + receive + {'DOWN',_,process,P,Exit1} -> + {error,on_load_failure} = Exit1 + end, + receive + {'DOWN',_,process,Pid2,Exit2} -> + io:format("~p\n", [Exit2]) + after 10000 -> + ct:fail(no_down_message) + end + end. + +on_load_self_call(_Config) -> + Mod = ?FUNCTION_NAME, + register(Mod, self()), + Tree = ?Q(["-module('@Mod@').\n", + "-export([ext/0]).\n", + "-on_load(f/0).\n", + "f() ->\n", + " '@Mod@' ! (catch '@Mod@':ext()),\n", + " ok.\n", + "ext() -> good_work.\n"]), + merl:print(Tree), + {ok,Mod,Code} = merl:compile(Tree), + + {'EXIT',{undef,_}} = on_load_do_load(Mod, Code), + good_work = on_load_do_load(Mod, Code), + + ok. + +on_load_do_load(Mod, Code) -> + spawn(fun() -> + {module,Mod} = code:load_binary(Mod, "", Code) + end), + receive + Any -> Any + end. + +on_load_pending(_Config) -> + Mod = ?FUNCTION_NAME, + Tree1 = ?Q(["-module('@Mod@').\n", + "-on_load(f/0).\n", + "f() ->\n", + " register('@Mod@', self()),\n", + " receive _ -> ok end.\n"]), + merl:print(Tree1), + {ok,Mod,Code1} = merl:compile(Tree1), + + Tree2 = ?Q(["-module('@Mod@').\n", + "-export([t/0]).\n", + "t() -> ok.\n"]), + merl:print(Tree2), + {ok,Mod,Code2} = merl:compile(Tree2), + + Self = self(), + {_,Ref1} = + spawn_monitor(fun() -> + Self ! started1, + {module,Mod} = code:load_binary(Mod, "", Code1) + end), + receive started1 -> ok end, + timer:sleep(10), + {_,Ref2} = + spawn_monitor(fun() -> + Self ! started2, + {module,Mod} = code:load_binary(Mod, "", Code2), + ok = Mod:t() + end), + receive started2 -> ok end, + receive + Unexpected -> + ct:fail({unexpected,Unexpected}) + after 100 -> + ok + end, + Mod ! go, + receive + {'DOWN',Ref1,process,_,normal} -> ok + end, + receive + {'DOWN',Ref2,process,_,normal} -> ok + end, + ok = Mod:t(), + ok. + +on_load_deleted(_Config) -> + Mod = ?FUNCTION_NAME, + + R0 = fun() -> + Tree = ?Q(["-module('@Mod@').\n", + "-on_load(f/0).\n", + "f() -> ok.\n"]), + merl:print(Tree), + {ok,Mod,Code} = merl:compile(Tree), + {module,Mod} = code:load_binary(Mod, "", Code) + end, + delete_before_reload(Mod, R0), + delete_before_reload(Mod, R0), + + R1 = fun() -> + Tree = ?Q(["-module('@Mod@').\n", + "-on_load(f/0).\n", + "f() -> fail.\n"]), + merl:print(Tree), + {ok,Mod,Code} = merl:compile(Tree), + {error,on_load_failure} = code:load_binary(Mod, "", Code) + end, + delete_before_reload(Mod, R1), + delete_before_reload(Mod, R1), + + OtherMod = list_to_atom(lists:concat([Mod,"_42"])), + OtherTree = ?Q(["-module('@OtherMod@').\n"]), + merl:print(OtherTree), + {ok,OtherMod,OtherCode} = merl:compile(OtherTree), + + R2 = fun() -> + RegName = 'on_load__registered_name', + Tree = ?Q(["-module('@Mod@').\n", + "-on_load(f/0).\n", + "f() ->\n", + " register('@RegName@', self()),\n", + " receive _ -> ok end.\n"]), + merl:print(Tree), + {ok,Mod,Code} = merl:compile(Tree), + spawn(fun() -> + {module,Mod} = code:load_binary(Mod, "", Code) + end), + receive after 1 -> ok end, + {module,OtherMod} = code:load_binary(OtherMod, "", + OtherCode), + RegName ! stop + end, + delete_before_reload(Mod, R2), + + ok. + +delete_before_reload(Mod, Reload) -> + false = check_old_code(Mod), + + Tree1 = ?Q(["-module('@Mod@').\n", + "-export([f/1]).\n", + "f(Parent) ->\n", + " register('@Mod@', self()),\n", + " Parent ! started,\n", + " receive _ -> ok end.\n"]), + merl:print(Tree1), + {ok,Mod,Code1} = merl:compile(Tree1), + + Self = self(), + spawn(fun() -> + {module,Mod} = code:load_binary(Mod, "", Code1), + Mod:f(Self) + end), + receive started -> ok end, + + true = code:delete(Mod), + true = check_old_code(Mod), + + Reload(), + + %% When loading the the module with the -on_load() function, + %% the reference to the old code would be lost. Make sure that + %% the old code is remembered and is still preventing the + %% purge. + false = code:soft_purge(Mod), + + %% Get rid of the old code. + Mod ! stop, + receive after 1 -> ok end, + true = code:soft_purge(Mod), + + %% Unload the version of the module with the -on_load() function. + true = code:delete(Mod), + true = code:soft_purge(Mod), + + ok. + + +%% Test that the native code of early loaded modules is loaded. native_early_modules(Config) when is_list(Config) -> case erlang:system_info(hipe_architecture) of undefined -> @@ -1641,11 +1798,205 @@ native_early_modules_1(Architecture) -> ok end. -get_mode(suite) -> []; -get_mode(doc) -> ["Test that the mode of the code server is properly retrieved"]; +%% Test that the mode of the code server is properly retrieved. get_mode(Config) when is_list(Config) -> interactive = code:get_mode(). +%% Make sure that the paths for all loaded modules have been normalized. +normalized_paths(_Config) -> + do_normalized_paths(erlang:loaded()). + +do_normalized_paths([M|Ms]) -> + case code:which(M) of + Special when is_atom(Special) -> + do_normalized_paths(Ms); + File when is_list(File) -> + File = filename:join([File]), + do_normalized_paths(Ms) + end; +do_normalized_paths([]) -> + ok. + +%% Test that module_status/1 behaves as expected +module_status(_Config) -> + case test_server:is_cover() of + true -> + module_status(); + false -> + %% Make sure that we terminate the cover server. + try + module_status() + after + cover:stop() + end + end. + +module_status() -> + %% basics + not_loaded = code:module_status(fubar), % nonexisting + {file, preloaded} = code:is_loaded(erlang), + loaded = code:module_status(erlang), % preloaded + loaded = code:module_status(?MODULE), % normal known loaded + + non_existing = code:which(?TESTMOD), % verify dummy name not in path + code:purge(?TESTMOD), % ensure no previous version in memory + code:delete(?TESTMOD), + code:purge(?TESTMOD), + + %% generated code is detected as such + {ok,?TESTMOD,Bin} = compile:forms(dummy_ast(), []), + {module,?TESTMOD} = code:load_binary(?TESTMOD,"",Bin), % no source file + ok = ?TESTMOD:f(), + "" = code:which(?TESTMOD), % verify empty string for source file + loaded = code:module_status(?TESTMOD), + + %% deleting generated code + true = code:delete(?TESTMOD), + non_existing = code:which(?TESTMOD), % verify still not in path + not_loaded = code:module_status(?TESTMOD), + + %% beam file exists but not loaded + make_source_file(<<"0">>), + compile_beam(0), + true = (non_existing =/= code:which(?TESTMOD)), % verify in path + not_loaded = code:module_status(?TESTMOD), + + %% loading code from disk makes it loaded + load_code(), + loaded = code:module_status(?TESTMOD), % loaded + + %% cover compiling a module + {ok,?TESTMOD} = cover:compile(?TESTMOD), + {file, cover_compiled} = code:is_loaded(?TESTMOD), % verify cover compiled + modified = code:module_status(?TESTMOD), % loaded cover code but file exists + remove_code(), + removed = code:module_status(?TESTMOD), % removed + compile_beam(0), + modified = code:module_status(?TESTMOD), % recreated + load_code(), + loaded = code:module_status(?TESTMOD), % loading removes cover status + code:purge(?TESTMOD), + true = code:delete(?TESTMOD), + not_loaded = code:module_status(?TESTMOD), % deleted + + %% recompilation ignores timestamps, only md5 matters + load_code(), + compile_beam(1100), + loaded = code:module_status(?TESTMOD), + + %% modifying module detects different md5 + make_source_file(<<"1">>), + compile_beam(0), + modified = code:module_status(?TESTMOD), + + %% loading the modified code from disk makes it loaded + load_code(), + loaded = code:module_status(?TESTMOD), + + %% removing and recreating a module with same md5 + remove_code(), + removed = code:module_status(?TESTMOD), + compile_beam(0), + loaded = code:module_status(?TESTMOD), + + case erlang:system_info(hipe_architecture) of + undefined -> + %% no native support + ok; + _ -> + %% native chunk is ignored if beam code is already loaded + load_code(), + loaded = code:module_status(?TESTMOD), + false = has_native(?TESTMOD), + compile_native(0), + BeamMD5 = erlang:get_module_info(?TESTMOD, md5), + {ok,{?TESTMOD,BeamMD5}} = beam_lib:md5(?TESTMODOBJ), % beam md5 unchanged + loaded = code:module_status(?TESTMOD), + + %% native code reported as loaded, though different md5 from beam + load_code(), + true = has_native(?TESTMOD), + NativeMD5 = erlang:get_module_info(?TESTMOD, md5), + true = (BeamMD5 =/= NativeMD5), + loaded = code:module_status(?TESTMOD), + + %% recompilation ignores timestamps, only md5 matters + compile_native(1100), % later timestamp + loaded = code:module_status(?TESTMOD), + + %% modifying native module detects different md5 + make_source_file(<<"2">>), + compile_native(0), + modified = code:module_status(?TESTMOD), + + %% loading the modified native code from disk makes it loaded + load_code(), + true = has_native(?TESTMOD), + NativeMD5_2 = erlang:get_module_info(?TESTMOD, md5), + true = (NativeMD5 =/= NativeMD5_2), % verify native md5 changed + {ok,{?TESTMOD,BeamMD5_2}} = beam_lib:md5(?TESTMODOBJ), + true = (BeamMD5_2 =/= NativeMD5_2), % verify md5 differs from beam + loaded = code:module_status(?TESTMOD), + + %% removing and recreating a native module with same md5 + remove_code(), + removed = code:module_status(?TESTMOD), + compile_native(0), + loaded = code:module_status(?TESTMOD), + + %% purging/deleting native module + code:purge(?TESTMOD), + true = code:delete(?TESTMOD), + not_loaded = code:module_status(?TESTMOD) + end, + ok. + +compile_beam(Sleep) -> + compile(Sleep, []). + +compile_native(Sleep) -> + compile(Sleep, [native]). + +compile(Sleep, Opts) -> + timer:sleep(Sleep), % increment compilation timestamp + {ok,?TESTMOD} = compile:file(?TESTMODSRC, Opts). + +load_code() -> + code:purge(?TESTMOD), + {module,?TESTMOD} = code:load_file(?TESTMOD). + +remove_code() -> + ok = file:delete(?TESTMODOBJ). + +has_native(Module) -> + case erlang:get_module_info(Module, native_addresses) of + [] -> false; + [_|_] -> true + end. + +make_source_file(Body) -> + ok = file:write_file(?TESTMODSRC, dummy_source(Body)). + +dummy_source(Body) -> + [<<"-module(" ?TESTMODSTR ").\n" + "-export([f/0]).\n" + "f() -> ">>, Body, <<".\n">>]. + +dummy_ast() -> + dummy_ast(?TESTMODSTR). + +dummy_ast(Mod) when is_atom(Mod) -> + dummy_ast(atom_to_list(Mod)); +dummy_ast(ModStr) -> + [scan_form("-module(" ++ ModStr ++ ")."), + scan_form("-export([f/0])."), + scan_form("f() -> ok.")]. + +scan_form(String) -> + {ok,Ts,_} = erl_scan:string(String), + {ok,F} = erl_parse:parse_form(Ts), + F. + %%----------------------------------------------------------------- %% error_logger handler. %% (Copied from stdlib/test/proc_lib_SUITE.erl.) @@ -1653,9 +2004,7 @@ get_mode(Config) when is_list(Config) -> init(Tester) -> {ok, Tester}. -handle_event({error, _GL, {emulator, _, _}}, Tester) -> - {ok, Tester}; -handle_event({error, _GL, Msg}, Tester) -> +handle_event({warning_msg, _GL, Msg}, Tester) -> Tester ! Msg, {ok, Tester}; handle_event(_Event, State) -> @@ -1674,7 +2023,7 @@ terminate(_Reason, State) -> %%% start_node(Name, Param) -> - ?t:start_node(Name, slave, [{args, Param}]). + test_server:start_node(Name, slave, [{args, Param}]). stop_node(Node) -> - ?t:stop_node(Node). + test_server:stop_node(Node). diff --git a/lib/kernel/test/code_SUITE_data/code_archive_dict-1.0/src/code_archive_dict.erl b/lib/kernel/test/code_SUITE_data/code_archive_dict-1.0/src/code_archive_dict.erl index ccc954ee17..1fcf05a0a1 100644 --- a/lib/kernel/test/code_SUITE_data/code_archive_dict-1.0/src/code_archive_dict.erl +++ b/lib/kernel/test/code_SUITE_data/code_archive_dict-1.0/src/code_archive_dict.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2009. All Rights Reserved. +%% Copyright Ericsson AB 2008-2016. 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/lib/kernel/test/code_SUITE_data/code_archive_dict-1.0/src/code_archive_dict_app.erl b/lib/kernel/test/code_SUITE_data/code_archive_dict-1.0/src/code_archive_dict_app.erl index a23ef7001d..d164db9794 100644 --- a/lib/kernel/test/code_SUITE_data/code_archive_dict-1.0/src/code_archive_dict_app.erl +++ b/lib/kernel/test/code_SUITE_data/code_archive_dict-1.0/src/code_archive_dict_app.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2009. All Rights Reserved. +%% Copyright Ericsson AB 2008-2016. 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/lib/kernel/test/code_SUITE_data/code_archive_dict-1.0/src/code_archive_dict_sup.erl b/lib/kernel/test/code_SUITE_data/code_archive_dict-1.0/src/code_archive_dict_sup.erl index 3e427ed34a..e659202e08 100644 --- a/lib/kernel/test/code_SUITE_data/code_archive_dict-1.0/src/code_archive_dict_sup.erl +++ b/lib/kernel/test/code_SUITE_data/code_archive_dict-1.0/src/code_archive_dict_sup.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2009. All Rights Reserved. +%% Copyright Ericsson AB 2008-2016. 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/lib/kernel/test/code_SUITE_data/mult_lib_roots/first_root/my_dummy_app-b/ebin/lists.erl b/lib/kernel/test/code_SUITE_data/mult_lib_roots/first_root/my_dummy_app-b/ebin/lists.erl index e97dde2703..6accd00191 100644 --- a/lib/kernel/test/code_SUITE_data/mult_lib_roots/first_root/my_dummy_app-b/ebin/lists.erl +++ b/lib/kernel/test/code_SUITE_data/mult_lib_roots/first_root/my_dummy_app-b/ebin/lists.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2009. All Rights Reserved. +%% Copyright Ericsson AB 2008-2016. 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/lib/kernel/test/code_SUITE_data/mult_lib_roots/first_root/my_dummy_app-c/ebin/code_SUITE_mult_root_module.erl b/lib/kernel/test/code_SUITE_data/mult_lib_roots/first_root/my_dummy_app-c/ebin/code_SUITE_mult_root_module.erl index 3c9cd75f34..f45e5b2dba 100644 --- a/lib/kernel/test/code_SUITE_data/mult_lib_roots/first_root/my_dummy_app-c/ebin/code_SUITE_mult_root_module.erl +++ b/lib/kernel/test/code_SUITE_data/mult_lib_roots/first_root/my_dummy_app-c/ebin/code_SUITE_mult_root_module.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2009. All Rights Reserved. +%% Copyright Ericsson AB 2008-2016. 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/lib/kernel/test/code_SUITE_data/on_load_errors/simple_on_load_error.erl b/lib/kernel/test/code_SUITE_data/on_load_errors/simple_on_load_error.erl new file mode 100644 index 0000000000..603c282257 --- /dev/null +++ b/lib/kernel/test/code_SUITE_data/on_load_errors/simple_on_load_error.erl @@ -0,0 +1,5 @@ +-module(simple_on_load_error). +-on_load(on_load/0). + +on_load() -> + nope. diff --git a/lib/kernel/test/code_SUITE_data/upgrade_client.erl b/lib/kernel/test/code_SUITE_data/upgrade_client.erl index bb655e01d3..1c3c2def53 100644 --- a/lib/kernel/test/code_SUITE_data/upgrade_client.erl +++ b/lib/kernel/test/code_SUITE_data/upgrade_client.erl @@ -9,6 +9,8 @@ run(Dir, Upgradee1, Upgradee2, Other1, Other2) -> %% Load version 1 of upgradee code_SUITE:compile_load(upgradee, Dir, 1, Upgradee1), + Tracer = start_tracing(), + ?line 1 = upgradee:exp1(), ?line 1 = upgradee:exp1exp2(), ?line 1 = upgradee:exp1loc2(), @@ -56,6 +58,15 @@ run(Dir, Upgradee1, Upgradee2, Other1, Other2) -> ?line {'EXIT',{undef,_}} = proxy_call(P, other, loc1), ?line {'EXIT',{undef,_}} = proxy_call(P, other, loc2), + Env1 = "Env1", + put(loc1_fun, upgradee:get_local_fun(Env1)), + ?line {1,Env1} = (get(loc1_fun))(), + + put(exp1exp2_fun, upgradee:get_exp1exp2_fun()), + ?line 1 = (get(exp1exp2_fun))(), + + ok = check_tracing(Tracer, 13), + %% %% Load version 1 of other %% @@ -78,6 +89,8 @@ run(Dir, Upgradee1, Upgradee2, Other1, Other2) -> ?line {'EXIT',{undef,_}} = proxy_call(P, other, exp2), ?line {'EXIT',{undef,_}} = proxy_call(P, other, loc2), + ok = check_tracing(Tracer, 5), + %% %% Load version 2 of upgradee %% @@ -130,6 +143,15 @@ run(Dir, Upgradee1, Upgradee2, Other1, Other2) -> ?line {'EXIT',{undef,_}} = proxy_call(P, other, exp2), ?line {'EXIT',{undef,_}} = proxy_call(P, other, loc2), + ?line {1,Env1} = (get(loc1_fun))(), + Env2 = "Env2", + put(loc2_fun, upgradee:get_local_fun(Env2)), + ?line {2,Env2} = (get(loc2_fun))(), + + ?line 2 = (get(exp1exp2_fun))(), + + ok = check_tracing(Tracer, 10), + %% %% Load version 2 of other %% @@ -182,17 +204,26 @@ run(Dir, Upgradee1, Upgradee2, Other1, Other2) -> ?line {'EXIT',{undef,_}} = proxy_call(P, other, loc1loc2), ?line {'EXIT',{undef,_}} = proxy_call(P, other, loc2), + ?line {1,Env1} = (get(loc1_fun))(), + ?line {2,Env2} = (get(loc2_fun))(), + ?line 2 = (get(exp1exp2_fun))(), + + ok = check_tracing(Tracer, 10), %% %% Upgrade proxy to version 2 %% P ! upgrade_order, - %% - io:format("Delete version 2 of 'upgradee'\n",[]), + io:format("Purge version 1 of 'upgradee'\n",[]), %% + put(loc1_fun,undefined), code:purge(upgradee), + + %% + io:format("Delete version 2 of 'upgradee'\n",[]), + %% code:delete(upgradee), ?line {'EXIT',{undef,_}} = (catch upgradee:exp2()), @@ -239,17 +270,24 @@ run(Dir, Upgradee1, Upgradee2, Other1, Other2) -> ?line {'EXIT',{undef,_}} = proxy_call(P, other, exp1loc2), ?line {'EXIT',{undef,_}} = proxy_call(P, other, loc1loc2), ?line {'EXIT',{undef,_}} = proxy_call(P, other, loc2), + + ?line {'EXIT',{undef,_}} = (catch (get(exp1exp2_fun))()), + ok = check_tracing(Tracer, 14), + unlink(P), exit(P, die_please), io:format("Purge 'upgradee'\n",[]), + put(loc2_fun,undefined), code:purge(upgradee), io:format("Delete and purge 'other'\n",[]), code:purge(other), code:delete(other), code:purge(other), + + stop_tracing(Tracer), ok. proxy_call(Pid, CallType, Func) -> @@ -257,3 +295,55 @@ proxy_call(Pid, CallType, Func) -> receive {Pid, call_result, Func, Ret} -> Ret end. + + +start_tracing() -> + Self = self(), + {Tracer,_} = spawn_opt(fun() -> tracer_loop(Self) end, [link,monitor]), + ?line 1 = erlang:trace_pattern({error_handler,undefined_function,3}, + true, [global]), + ?line 1 = erlang:trace(Self, true, [call,{tracer,Tracer}]), + Tracer. + + +tracer_loop(Receiver) -> + receive + die_please -> + ok; + {do_trace_delivered, Tracee} -> + _ = erlang:trace_delivered(Tracee), + tracer_loop(Receiver); + + Msg -> + Receiver ! Msg, + tracer_loop(Receiver) + end. + +check_tracing(Tracer, Expected) -> + Tracer ! {do_trace_delivered, self()}, + case check_tracing_loop(0,[]) of + {Expected,_} -> + ok; + {Got, MsgList} -> + io:format("Expected ~p trace msg, got ~p:\n~p\n", + [Expected, Got, lists:reverse(MsgList)]), + "Trace msg mismatch" + end. + +check_tracing_loop(N, MsgList) -> + Self = self(), + receive + {trace, _Pid, call, {_M, _F, _Args}} = Msg -> + check_tracing_loop(N+1, [Msg | MsgList]); + {trace_delivered, Self, _} -> + {N, MsgList} + end. + + +stop_tracing(Tracer) -> + erlang:trace_pattern({error_handler,undefined_function,3}, false, [global]), + erlang:trace(self(), false, [call]), + Tracer ! die_please, + receive + {'DOWN', _, process, Tracer, _} -> ok + end. diff --git a/lib/kernel/test/code_SUITE_data/upgradee.erl b/lib/kernel/test/code_SUITE_data/upgradee.erl index 62b1d95e30..8ca660c19c 100644 --- a/lib/kernel/test/code_SUITE_data/upgradee.erl +++ b/lib/kernel/test/code_SUITE_data/upgradee.erl @@ -8,6 +8,9 @@ -export([exp1/0]). % only exported in v1 -export([exp1loc2/0]). % exported in v1, local in v2 -export([exp1exp2/0]). % exported in v1 and v2 +-export([get_local_fun/1]). +-export([get_exp1exp2_fun/0]). +-export([exp1exp2_fun/0]). exp1() -> ?VERSION. loc1() -> ?VERSION. @@ -20,6 +23,9 @@ loc1() -> ?VERSION. -export([exp2/0]). -export([loc1exp2/0]). -export([exp1exp2/0]). +-export([get_local_fun/1]). +-export([get_exp1exp2_fun/0]). +-export([exp1exp2_fun/0]). exp2() -> ?VERSION. loc2() -> ?VERSION. @@ -31,6 +37,12 @@ exp1loc2() -> ?VERSION. loc1exp2() -> ?VERSION. loc1loc2() -> ?VERSION. +get_local_fun(Env) -> fun() -> {?VERSION,Env} end. +get_exp1exp2_fun() -> fun ?MODULE:exp1exp2_fun/0. + +exp1exp2_fun() -> + ?VERSION. + dispatch_loop() -> receive upgrade_order -> diff --git a/lib/kernel/test/code_a_test.erl b/lib/kernel/test/code_a_test.erl index 22830fff53..bb5d814f04 100644 --- a/lib/kernel/test/code_a_test.erl +++ b/lib/kernel/test/code_a_test.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2010. All Rights Reserved. +%% Copyright Ericsson AB 1996-2016. 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/lib/kernel/test/code_b_test.erl b/lib/kernel/test/code_b_test.erl index a8ff570e2e..9c362eb0d0 100644 --- a/lib/kernel/test/code_b_test.erl +++ b/lib/kernel/test/code_b_test.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2010. All Rights Reserved. +%% Copyright Ericsson AB 1996-2016. 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/lib/kernel/test/disk_log_SUITE.erl b/lib/kernel/test/disk_log_SUITE.erl index f55af1e354..9704c3b28c 100644 --- a/lib/kernel/test/disk_log_SUITE.erl +++ b/lib/kernel/test/disk_log_SUITE.erl @@ -1,24 +1,25 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2013. All Rights Reserved. +%% Copyright Ericsson AB 1997-2018. 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/. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at %% -%% 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. +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% -module(disk_log_SUITE). -%-define(debug, true). +%%-define(debug, true). -ifdef(debug). -define(format(S, A), io:format(S, A)). @@ -28,10 +29,10 @@ -define(config(X,Y), foo). -define(t,test_server). -else. --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -define(format(S, A), ok). --define(privdir(Conf), ?config(priv_dir, Conf)). --define(datadir(Conf), ?config(data_dir, Conf)). +-define(privdir(Conf), proplists:get_value(priv_dir, Conf)). +-define(datadir(Conf), proplists:get_value(data_dir, Conf)). -endif. -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, @@ -88,8 +89,6 @@ dist_terminate/1, dist_accessible/1, dist_deadlock/1, dist_open2/1, other_groups/1, - evil/1, - otp_6278/1, otp_10131/1]). -export([head_fun/1, hf/0, lserv/1, @@ -101,8 +100,6 @@ -export([client/4]). --define(default_timeout, ?t:minutes(1)). - %% error_logger -export([init/1, handle_event/2, handle_call/2, handle_info/2, @@ -124,7 +121,7 @@ [halt_int, wrap_int, halt_ext, wrap_ext, read_mode, head, notif, new_idx_vsn, reopen, block, unblock, open, close, error, chunk, truncate, many_users, info, change_size, - change_attribute, distribution, evil, otp_6278, otp_10131]). + change_attribute, distribution, otp_6278, otp_10131]). %% These test cases should be skipped if the VxWorks card is %% configured without NFS cache. @@ -138,7 +135,9 @@ change_size_after, default_size]). -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap,{minutes,2}}]. all() -> [{group, halt_int}, {group, wrap_int}, @@ -148,7 +147,7 @@ all() -> {group, open}, {group, close}, {group, error}, chunk, truncate, many_users, {group, info}, {group, change_size}, change_attribute, - {group, distribution}, evil, otp_6278, otp_10131]. + {group, distribution}, otp_6278, otp_10131]. groups() -> [{halt_int, [], [halt_int_inf, {group, halt_int_sz}]}, @@ -193,264 +192,234 @@ end_per_group(_GroupName, Config) -> -init_per_testcase(Case, Config) -> - case should_skip(Case,Config) of - true -> - CS = check_nfs(Config), - {skipped, lists:flatten - (io_lib:format - ("The test does not work " - "with current NFS cache size (~w)," - " to get this test to run, " - "~s the NFS cache size~n", - [CS, case CS of - 0 -> - "enlarge"; - _ -> - "zero" - end]))}; - _ -> - Dog=?t:timetrap(?t:minutes(2)), - [{watchdog, Dog}|Config] - end. +init_per_testcase(_Case, Config) -> + Config. -end_per_testcase(_Case, Config) -> - Dog=?config(watchdog, Config), - test_server:timetrap_cancel(Dog), +end_per_testcase(_Case, _Config) -> ok. -halt_int_inf(suite) -> []; -halt_int_inf(doc) -> ["Test simple halt disk log, size infinity"]; +%% Test simple halt disk log, size infinity. halt_int_inf(Conf) when is_list(Conf) -> Dir = ?privdir(Conf), - ?line ok = disk_log:start(), + ok = disk_log:start(), File = filename:join(Dir, "a.LOG"), - ?line {ok, a} = disk_log:open([{name,a}, {type,halt}, {size,infinity}, - {format,internal}, - {file, File}]), - ?line simple_log(a), - ?line ok = disk_log:close(a), - ?line ok = file:delete(File). + {ok, a} = disk_log:open([{name,a}, {type,halt}, {size,infinity}, + {format,internal}, + {file, File}]), + simple_log(a), + ok = disk_log:close(a), + ok = file:delete(File). -halt_int_sz_1(suite) -> []; -halt_int_sz_1(doc) -> ["Test simple halt disk log, size defined"]; +%% Test simple halt disk log, size defined. halt_int_sz_1(Conf) when is_list(Conf) -> Dir = ?privdir(Conf), File = filename:join(Dir, "a.LOG"), - ?line {ok, a} = disk_log:open([{name,a}, {type,halt}, {size,18000}, - {format,internal}, - {file, File}]), - ?line simple_log(a), - ?line ok = disk_log:truncate(a), - ?line [] = get_all_terms(a), + {ok, a} = disk_log:open([{name,a}, {type,halt}, {size,18000}, + {format,internal}, + {file, File}]), + simple_log(a), + ok = disk_log:truncate(a), + [] = get_all_terms(a), T1 = mk_bytes(10000), T2 = mk_bytes(5000), - ?line ok = disk_log:log(a, T1), - ?line case get_all_terms(a) of - [T1] -> - ok; - E1 -> - test_server_fail({bad_terms, E1, [T1]}) - end, - ?line ok = disk_log:log(a, T2), - ?line {error, {full, a}} = disk_log:log(a, T1), - ?line ok = disk_log:alog(a, T1), - ?line case get_all_terms(a) of - [T1, T2] -> - ok; - E2 -> - test_server_fail({bad_terms, E2, [T1, T2]}) - end, - ?line ok = disk_log:close(a), - ?line ok = file:delete(File). + ok = disk_log:log(a, T1), + case get_all_terms(a) of + [T1] -> + ok; + E1 -> + test_server_fail({bad_terms, E1, [T1]}) + end, + ok = disk_log:log(a, T2), + {error, {full, a}} = disk_log:log(a, T1), + ok = disk_log:alog(a, T1), + case get_all_terms(a) of + [T1, T2] -> + ok; + E2 -> + test_server_fail({bad_terms, E2, [T1, T2]}) + end, + ok = disk_log:close(a), + ok = file:delete(File). -halt_int_sz_2(suite) -> []; -halt_int_sz_2(doc) -> ["Test simple halt disk log, size ~8192"]; +%% Test simple halt disk log, size ~8192. halt_int_sz_2(Conf) when is_list(Conf) -> Dir = ?privdir(Conf), File1 = filename:join(Dir, "a.LOG"), File2 = filename:join(Dir, "b.LOG"), File3 = filename:join(Dir, "c.LOG"), - ?line {ok, a} = disk_log:open([{name,a}, {type,halt}, {size,8191}, - {format,internal}, - {file, File1}]), - ?line {ok, b} = disk_log:open([{name,b}, {type,halt}, {size,8192}, - {format,internal}, - {file, File2}]), - ?line {ok, c} = disk_log:open([{name,c}, {type,halt}, {size,8193}, - {format,internal}, - {file, File3}]), + {ok, a} = disk_log:open([{name,a}, {type,halt}, {size,8191}, + {format,internal}, + {file, File1}]), + {ok, b} = disk_log:open([{name,b}, {type,halt}, {size,8192}, + {format,internal}, + {file, File2}]), + {ok, c} = disk_log:open([{name,c}, {type,halt}, {size,8193}, + {format,internal}, + {file, File3}]), T1 = mk_bytes(8191-16), % 16 is size of header + magics for 1 item T2 = mk_bytes(8192-16), T3 = mk_bytes(8193-16), - ?line ok = disk_log:log(a, T1), - ?line ok = disk_log:log(b, T2), - ?line ok = disk_log:log(c, T3), - ?line case get_all_terms(a) of - [T1] -> - ok; - E1 -> - test_server_fail({bad_terms, E1, [T1]}) - end, - ?line case get_all_terms(b) of - [T2] -> - ok; - E2 -> - test_server_fail({bad_terms, E2, [T2]}) - end, - ?line case get_all_terms(c) of - [T3] -> - ok; - E3 -> - test_server_fail({bad_terms, E3, [T3]}) - end, - ?line ok = disk_log:truncate(a), - ?line ok = disk_log:truncate(b), - ?line {error, {full, a}} = disk_log:log(a, T2), - ?line {error, {full, b}} = disk_log:log(b, T3), - ?line [] = get_all_terms(a), - ?line [] = get_all_terms(b), - ?line ok = disk_log:close(a), - ?line ok = disk_log:close(b), - ?line ok = disk_log:close(c), - ?line ok = file:delete(File1), - ?line ok = file:delete(File2), - ?line ok = file:delete(File3), + ok = disk_log:log(a, T1), + ok = disk_log:log(b, T2), + ok = disk_log:log(c, T3), + case get_all_terms(a) of + [T1] -> + ok; + E1 -> + test_server_fail({bad_terms, E1, [T1]}) + end, + case get_all_terms(b) of + [T2] -> + ok; + E2 -> + test_server_fail({bad_terms, E2, [T2]}) + end, + case get_all_terms(c) of + [T3] -> + ok; + E3 -> + test_server_fail({bad_terms, E3, [T3]}) + end, + ok = disk_log:truncate(a), + ok = disk_log:truncate(b), + {error, {full, a}} = disk_log:log(a, T2), + {error, {full, b}} = disk_log:log(b, T3), + [] = get_all_terms(a), + [] = get_all_terms(b), + ok = disk_log:close(a), + ok = disk_log:close(b), + ok = disk_log:close(c), + ok = file:delete(File1), + ok = file:delete(File2), + ok = file:delete(File3), ok. -halt_int_ro(suite) -> []; -halt_int_ro(doc) -> ["Test simple halt disk log, read only, internal"]; +%% Test simple halt disk log, read only, internal. halt_int_ro(Conf) when is_list(Conf) -> Dir = ?privdir(Conf), File = filename:join(Dir, "a.LOG"), - ?line {ok, a} = disk_log:open([{name,a}, {type,halt}, {size,infinity}, - {format,internal}, {file, File}]), + {ok, a} = disk_log:open([{name,a}, {type,halt}, {size,infinity}, + {format,internal}, {file, File}]), simple_log(a), - ?line ok = disk_log:close(a), + ok = disk_log:close(a), - ?line {ok, a} = disk_log:open([{name,a}, {type,halt}, {size,infinity}, - {format,internal}, {file, File}, - {mode,read_only}]), + {ok, a} = disk_log:open([{name,a}, {type,halt}, {size,infinity}, + {format,internal}, {file, File}, + {mode,read_only}]), T1 = "not allowed to write", - ?line {error, {read_only_mode, a}} = disk_log:log(a, T1), - ?line ok = disk_log:close(a), - ?line ok = file:delete(File). + {error, {read_only_mode, a}} = disk_log:log(a, T1), + ok = disk_log:close(a), + ok = file:delete(File). -halt_ext_ro(suite) -> []; -halt_ext_ro(doc) -> ["Test simple halt disk log, read only, external"]; +%% Test simple halt disk log, read only, external. halt_ext_ro(Conf) when is_list(Conf) -> Dir = ?privdir(Conf), File = filename:join(Dir, "a.LOG"), - ?line {ok, a} = disk_log:open([{name,a}, {type,halt}, {size,infinity}, - {format,external}, {file, File}]), + {ok, a} = disk_log:open([{name,a}, {type,halt}, {size,infinity}, + {format,external}, {file, File}]), xsimple_log(File, a), - ?line ok = disk_log:close(a), - ?line {ok, a} = disk_log:open([{name,a}, {type,halt}, {size,infinity}, - {format,external}, {file, File}, - {mode,read_only}]), + ok = disk_log:close(a), + {ok, a} = disk_log:open([{name,a}, {type,halt}, {size,infinity}, + {format,external}, {file, File}, + {mode,read_only}]), T1 = "not allowed to write", - ?line {error, {read_only_mode, a}} = disk_log:blog(a, T1), - ?line ok = disk_log:close(a), - ?line ok = file:delete(File). + {error, {read_only_mode, a}} = disk_log:blog(a, T1), + ok = disk_log:close(a), + ok = file:delete(File). -wrap_int_ro(suite) -> []; -wrap_int_ro(doc) -> ["Test simple wrap disk log, read only, internal"]; +%% Test simple wrap disk log, read only, internal. wrap_int_ro(Conf) when is_list(Conf) -> Dir = ?privdir(Conf), File = filename:join(Dir, "a.LOG"), - ?line {ok, a} = disk_log:open([{name,a}, {type,wrap}, {size,{8000, 4}}, - {format,internal}, {file, File}]), + {ok, a} = disk_log:open([{name,a}, {type,wrap}, {size,{8000, 4}}, + {format,internal}, {file, File}]), simple_log(a), - ?line ok = disk_log:close(a), - ?line {ok, a} = disk_log:open([{name,a}, {type,wrap}, {size,{8000, 4}}, - {format,internal}, {file, File}, {mode,read_only}]), + ok = disk_log:close(a), + {ok, a} = disk_log:open([{name,a}, {type,wrap}, {size,{8000, 4}}, + {format,internal}, {file, File}, {mode,read_only}]), T1 = "not allowed to write", - ?line {error, {read_only_mode, a}} = disk_log:log(a, T1), - ?line ok = disk_log:close(a), - ?line del(File, 4). + {error, {read_only_mode, a}} = disk_log:log(a, T1), + ok = disk_log:close(a), + del(File, 4). -wrap_ext_ro(suite) -> []; -wrap_ext_ro(doc) -> ["Test simple wrap disk log, read only, external"]; +%% Test simple wrap disk log, read only, external. wrap_ext_ro(Conf) when is_list(Conf) -> Dir = ?privdir(Conf), File = filename:join(Dir, "a.LOG"), - ?line {ok, a} = disk_log:open([{name,a}, {type,wrap}, {size,{8000, 4}}, - {format,external}, {file, File}]), + {ok, a} = disk_log:open([{name,a}, {type,wrap}, {size,{8000, 4}}, + {format,external}, {file, File}]), x2simple_log(File ++ ".1", a), - ?line ok = disk_log:close(a), - ?line {ok, a} = disk_log:open([{name,a}, {type,wrap}, {size,{8000, 4}}, - {format,external}, {file, File}, - {mode,read_only}]), + ok = disk_log:close(a), + {ok, a} = disk_log:open([{name,a}, {type,wrap}, {size,{8000, 4}}, + {format,external}, {file, File}, + {mode,read_only}]), T1 = "not allowed to write", - ?line {error, {read_only_mode, a}} = disk_log:blog(a, T1), - ?line {error, {read_only_mode, a}} = disk_log:inc_wrap_file(a), - ?line ok = disk_log:close(a), + {error, {read_only_mode, a}} = disk_log:blog(a, T1), + {error, {read_only_mode, a}} = disk_log:inc_wrap_file(a), + ok = disk_log:close(a), del(File, 4). -halt_trunc(suite) -> []; -halt_trunc(doc) -> ["Test truncation of halt disk log"]; +%% Test truncation of halt disk log. halt_trunc(Conf) when is_list(Conf) -> Dir = ?privdir(Conf), File = filename:join(Dir, "a.LOG"), - ?line {ok, a} = disk_log:open([{name,a}, {type,halt}, {size,infinity}, - {format,internal}, {file, File}]), + {ok, a} = disk_log:open([{name,a}, {type,halt}, {size,infinity}, + {format,internal}, {file, File}]), simple_log(a), - ?line ok = disk_log:close(a), - ?line {error,{badarg,repair_read_only}} = + ok = disk_log:close(a), + {error,{badarg,repair_read_only}} = disk_log:open([{name,a}, {type,halt}, {size,infinity}, {repair, truncate}, {format,internal}, {file, File}, {mode,read_only}]), - ?line ok = file:delete(File). + ok = file:delete(File). -halt_misc(suite) -> []; -halt_misc(doc) -> ["Test truncation of halt disk log"]; +%% Test truncation of halt disk log. halt_misc(Conf) when is_list(Conf) -> Dir = ?privdir(Conf), File = filename:join(Dir, "a.LOG"), - ?line {ok, a} = disk_log:open([{name,a}, {type,halt}, {size,infinity}, - {format,internal}, {file, File}]), + {ok, a} = disk_log:open([{name,a}, {type,halt}, {size,infinity}, + {format,internal}, {file, File}]), simple_log(a), - ?line ok = disk_log:close(a), - ?line {ok, a} = disk_log:open([{name,a}, {type,halt}, {size,infinity}, - {format,internal}, {file, File}, - {mode,read_only}]), + ok = disk_log:close(a), + {ok, a} = disk_log:open([{name,a}, {type,halt}, {size,infinity}, + {format,internal}, {file, File}, + {mode,read_only}]), T1 = "not allowed to write", - ?line {error, {read_only_mode, a}} = disk_log:log(a, T1), - ?line {error, {read_only_mode, a}} = disk_log:sync(a), - ?line {error, {read_only_mode, a}} = disk_log:reopen(a, "b.LOG"), - ?line {error, {read_only_mode, a}} = + {error, {read_only_mode, a}} = disk_log:log(a, T1), + {error, {read_only_mode, a}} = disk_log:sync(a), + {error, {read_only_mode, a}} = disk_log:reopen(a, "b.LOG"), + {error, {read_only_mode, a}} = disk_log:change_header(a, {head,header}), - ?line {error, {read_only_mode, a}} = + {error, {read_only_mode, a}} = disk_log:change_size(a, infinity), - ?line ok = disk_log:close(a), - ?line ok = file:delete(File). + ok = disk_log:close(a), + ok = file:delete(File). -halt_ro_alog(suite) -> []; -halt_ro_alog(doc) -> ["Test truncation of halt disk log, read only"]; +%% Test truncation of halt disk log, read only. halt_ro_alog(Conf) when is_list(Conf) -> Dir = ?privdir(Conf), File = filename:join(Dir, "a.LOG"), - ?line {ok, a} = disk_log:open([{name,a}, {type,halt}, {size,infinity}, - {format,internal}, {file, File}]), + {ok, a} = disk_log:open([{name,a}, {type,halt}, {size,infinity}, + {format,internal}, {file, File}]), simple_log(a), - ?line ok = disk_log:close(a), - ?line {ok, a} = disk_log:open([{name,a}, {type,halt}, {size,infinity}, - {notify,true}, {format,internal}, - {file, File}, {mode,read_only}]), + ok = disk_log:close(a), + {ok, a} = disk_log:open([{name,a}, {type,halt}, {size,infinity}, + {notify,true}, {format,internal}, + {file, File}, {mode,read_only}]), T1 = "not allowed to write", - ?line ok = disk_log:alog(a, T1), - ?line ok = halt_ro_alog_wait_notify(a, T1), - ?line ok = disk_log:close(a), - ?line ok = file:delete(File). + ok = disk_log:alog(a, T1), + ok = halt_ro_alog_wait_notify(a, T1), + ok = disk_log:close(a), + ok = file:delete(File). halt_ro_alog_wait_notify(Log, T) -> Term = term_to_binary(T), receive - {disk_log, _, Log,{read_only, Term}} -> + {disk_log, _, Log,{read_only, [Term]}} -> ok; Other -> Other @@ -458,28 +427,27 @@ halt_ro_alog_wait_notify(Log, T) -> failed end. -halt_ro_balog(suite) -> []; -halt_ro_balog(doc) -> ["Test truncation of halt disk log, read only"]; +%% Test truncation of halt disk log, read only. halt_ro_balog(Conf) when is_list(Conf) -> Dir = ?privdir(Conf), File = filename:join(Dir, "a.LOG"), - ?line {ok, a} = disk_log:open([{name,a}, {type,halt}, {size,infinity}, - {format,internal}, {file, File}]), + {ok, a} = disk_log:open([{name,a}, {type,halt}, {size,infinity}, + {format,internal}, {file, File}]), simple_log(a), - ?line ok = disk_log:close(a), - ?line {ok, a} = disk_log:open([{name,a}, {type,halt}, {size,infinity}, - {notify,true}, {format,external}, - {file, File}, {mode,read_only}]), + ok = disk_log:close(a), + {ok, a} = disk_log:open([{name,a}, {type,halt}, {size,infinity}, + {notify,true}, {format,external}, + {file, File}, {mode,read_only}]), T1 = "not allowed to write", - ?line ok = disk_log:balog(a, T1), - ?line ok = halt_ro_balog_wait_notify(a, T1), - ?line ok = disk_log:close(a), - ?line ok = file:delete(File). + ok = disk_log:balog(a, T1), + ok = halt_ro_balog_wait_notify(a, T1), + ok = disk_log:close(a), + ok = file:delete(File). halt_ro_balog_wait_notify(Log, T) -> Term = list_to_binary(T), receive - {disk_log, _, Log,{read_only, Term}} -> + {disk_log, _, Log,{read_only, [Term]}} -> ok; Other -> Other @@ -487,127 +455,123 @@ halt_ro_balog_wait_notify(Log, T) -> failed end. -halt_ro_crash(suite) -> []; -halt_ro_crash(doc) -> ["Test truncation of halt disk log, read only, repair"]; +%% Test truncation of halt disk log, read only, repair. halt_ro_crash(Conf) when is_list(Conf) -> Dir = ?privdir(Conf), File = filename:join(Dir, "a.LOG"), - ?line file:delete(File), - ?line {ok, a} = disk_log:open([{name,a}, {type,halt}, {size,infinity}, - {format,internal},{file, File}]), + file:delete(File), + {ok, a} = disk_log:open([{name,a}, {type,halt}, {size,infinity}, + {format,internal},{file, File}]), simple_log(a), - ?line ok = disk_log:close(a), + ok = disk_log:close(a), crash(File, 10), - ?line {ok, a} = disk_log:open([{name,a}, {type,halt}, {size,infinity}, - {notify,true}, {format,internal}, - {file, File}, {mode,read_only}]), + {ok, a} = disk_log:open([{name,a}, {type,halt}, {size,infinity}, + {notify,true}, {format,internal}, + {file, File}, {mode,read_only}]), - ?line Error1 = {error, {read_only_mode, a}} = disk_log:truncate(a), - ?line "The disk log" ++ _ = format_error(Error1), + Error1 = {error, {read_only_mode, a}} = disk_log:truncate(a), + "The disk log" ++ _ = format_error(Error1), %% crash/1 sets the length of the first item to something big (2.5 kb). %% In R6B, binary_to_term accepts garbage at the end of the binary, %% which means that the first item is recognized! %% This is how it was before R6B: - %% ?line {C1,T1,15} = disk_log:chunk(a,start), - %% ?line {C2,T2} = disk_log:chunk(a,C1), + %% {C1,T1,15} = disk_log:chunk(a,start), + %% {C2,T2} = disk_log:chunk(a,C1), {C1,_OneItem,7478} = disk_log:chunk(a,start), {C2, [], 7} = disk_log:chunk(a,C1), - ?line eof = disk_log:chunk(a,C2), - ?line ok = disk_log:close(a), - ?line ok = file:delete(File). + eof = disk_log:chunk(a,C2), + ok = disk_log:close(a), + ok = file:delete(File). -wrap_int_1(suite) -> []; -wrap_int_1(doc) -> ["Test wrap disk log, internal"]; +%% Test wrap disk log, internal. wrap_int_1(Conf) when is_list(Conf) -> Dir = ?privdir(Conf), File = filename:join(Dir, "a.LOG"), - ?line {ok, a} = disk_log:open([{name,a}, {type,wrap}, {size,{8000, 4}}, - {format,internal}, - {file, File}]), - ?line [_] = + {ok, a} = disk_log:open([{name,a}, {type,wrap}, {size,{8000, 4}}, + {format,internal}, + {file, File}]), + [_] = lists:filter(fun(P) -> disk_log:pid2name(P) =/= undefined end, erlang:processes()), simple_log(a), - ?line ok = disk_log:close(a), + ok = disk_log:close(a), del(File, 4), - ?line {ok, a} = disk_log:open([{name,a}, {type,wrap}, {size,{8000, 4}}, - {format,internal}, - {file, File}]), - ?line [] = get_all_terms(a), + {ok, a} = disk_log:open([{name,a}, {type,wrap}, {size,{8000, 4}}, + {format,internal}, + {file, File}]), + [] = get_all_terms(a), T1 = mk_bytes(10000), % file 2 T2 = mk_bytes(5000), % file 3 T3 = mk_bytes(4000), % file 4 T4 = mk_bytes(2000), % file 4 T5 = mk_bytes(5000), % file 1 T6 = mk_bytes(5000), % file 2 - ?line ok = disk_log:log(a, T1), - ?line ok = disk_log:log(a, T2), - ?line ok = disk_log:log(a, T3), - ?line ok = disk_log:log_terms(a, [T4, T5, T6]), - ?line case get_all_terms(a) of - [T2,T3,T4,T5,T6] -> - ok; - E1 -> - test_server_fail({bad_terms, E1, [T2,T3,T4,T5,T6]}) - end, - ?line ok = disk_log:close(a), + ok = disk_log:log(a, T1), + ok = disk_log:log(a, T2), + ok = disk_log:log(a, T3), + ok = disk_log:log_terms(a, [T4, T5, T6]), + case get_all_terms(a) of + [T2,T3,T4,T5,T6] -> + ok; + E1 -> + test_server_fail({bad_terms, E1, [T2,T3,T4,T5,T6]}) + end, + ok = disk_log:close(a), del(File, 4). -wrap_int_2(suite) -> []; -wrap_int_2(doc) -> ["Test wrap disk log, internal"]; +%% Test wrap disk log, internal. wrap_int_2(Conf) when is_list(Conf) -> Dir = ?privdir(Conf), File1 = filename:join(Dir, "a.LOG"), File2 = filename:join(Dir, "b.LOG"), File3 = filename:join(Dir, "c.LOG"), - ?line {ok, a} = disk_log:open([{name,a}, {type,wrap}, {size,{8191,3}}, - {format,internal}, - {file, File1}]), - ?line {ok, b} = disk_log:open([{name,b}, {type,wrap}, {size,{8192,3}}, - {format,internal}, - {file, File2}]), - ?line {ok, c} = disk_log:open([{name,c}, {type,wrap}, {size,{8193,3}}, - {format,internal}, - {file, File3}]), + {ok, a} = disk_log:open([{name,a}, {type,wrap}, {size,{8191,3}}, + {format,internal}, + {file, File1}]), + {ok, b} = disk_log:open([{name,b}, {type,wrap}, {size,{8192,3}}, + {format,internal}, + {file, File2}]), + {ok, c} = disk_log:open([{name,c}, {type,wrap}, {size,{8193,3}}, + {format,internal}, + {file, File3}]), T1 = mk_bytes(8191-16), % 16 is size of header + magics for 1 item T2 = mk_bytes(8192-16), T3 = mk_bytes(8193-16), - ?line ok = disk_log:log(a, T1), - ?line ok = disk_log:log(b, T2), - ?line ok = disk_log:log(c, T3), - ?line case get_all_terms(a) of - [T1] -> - ok; - E1 -> - test_server_fail({bad_terms, E1, [T1]}) - end, - ?line case get_all_terms(b) of - [T2] -> - ok; - E2 -> - test_server_fail({bad_terms, E2, [T2]}) - end, - ?line case get_all_terms(c) of - [T3] -> - ok; - E3 -> - test_server_fail({bad_terms, E3, [T3]}) - end, - ?line ok = disk_log:close(a), - ?line ok = disk_log:close(b), - ?line ok = disk_log:close(c), + ok = disk_log:log(a, T1), + ok = disk_log:log(b, T2), + ok = disk_log:log(c, T3), + case get_all_terms(a) of + [T1] -> + ok; + E1 -> + test_server_fail({bad_terms, E1, [T1]}) + end, + case get_all_terms(b) of + [T2] -> + ok; + E2 -> + test_server_fail({bad_terms, E2, [T2]}) + end, + case get_all_terms(c) of + [T3] -> + ok; + E3 -> + test_server_fail({bad_terms, E3, [T3]}) + end, + ok = disk_log:close(a), + ok = disk_log:close(b), + ok = disk_log:close(c), del(File1, 3), del(File2, 3), del(File3, 3). -inc_wrap_file(suite) -> []; -inc_wrap_file(doc) -> ["Test disk log, force a change to next file"]; +%% Test disk log, force a change to next file. inc_wrap_file(Conf) when is_list(Conf) -> Dir = ?privdir(Conf), File1 = filename:join(Dir, "a.LOG"), @@ -615,262 +579,257 @@ inc_wrap_file(Conf) when is_list(Conf) -> File3 = filename:join(Dir, "c.LOG"), %% Test that halt logs gets an error message - ?line {ok, a} = disk_log:open([{name, a}, {type, halt}, - {format, internal}, - {file, File1}]), - ?line ok = disk_log:log(a, "message one"), - ?line {error, {halt_log, a}} = disk_log:inc_wrap_file(a), + {ok, a} = disk_log:open([{name, a}, {type, halt}, + {format, internal}, + {file, File1}]), + ok = disk_log:log(a, "message one"), + {error, {halt_log, a}} = disk_log:inc_wrap_file(a), %% test an internally formatted wrap log file - ?line {ok, b} = disk_log:open([{name, b}, {type, wrap}, {size, {100,3}}, - {format, internal}, {head, 'thisisahead'}, - {file, File2}]), - ?line ok = disk_log:log(b, "message one"), - ?line ok = disk_log:inc_wrap_file(b), - ?line ok = disk_log:log(b, "message two"), - ?line ok = disk_log:inc_wrap_file(b), - ?line ok = disk_log:log(b, "message three"), - ?line ok = disk_log:inc_wrap_file(b), - ?line ok = disk_log:log(b, "message four"), - ?line T1 = get_all_terms(b), - ?line ['thisisahead', "message two", - 'thisisahead', "message three", - 'thisisahead', "message four"] = T1, + {ok, b} = disk_log:open([{name, b}, {type, wrap}, {size, {100,3}}, + {format, internal}, {head, 'thisisahead'}, + {file, File2}]), + ok = disk_log:log(b, "message one"), + ok = disk_log:inc_wrap_file(b), + ok = disk_log:log(b, "message two"), + ok = disk_log:inc_wrap_file(b), + ok = disk_log:log(b, "message three"), + ok = disk_log:inc_wrap_file(b), + ok = disk_log:log(b, "message four"), + T1 = get_all_terms(b), + ['thisisahead', "message two", + 'thisisahead', "message three", + 'thisisahead', "message four"] = T1, %% test an externally formatted wrap log file - ?line {ok, c} = disk_log:open([{name, c}, {type, wrap}, {size, {100,3}}, - {format,external}, {head,"this is a head "}, - {file, File3}]), - ?line ok = disk_log:blog(c, "message one"), - ?line ok = disk_log:inc_wrap_file(c), - ?line ok = disk_log:blog(c, "message two"), - ?line ok = disk_log:inc_wrap_file(c), - ?line ok = disk_log:blog(c, "message three"), - ?line ok = disk_log:inc_wrap_file(c), - ?line ok = disk_log:blog(c, "message four"), - ?line ok = disk_log:sync(c), - ?line {ok, Fd31} = file:open(File3 ++ ".1", [read]), - ?line {ok,"this is a head message four"} = file:read(Fd31, 200), - ?line {ok, Fd32} = file:open(File3 ++ ".2", [read]), - ?line {ok,"this is a head message two"} = file:read(Fd32, 200), - ?line {ok, Fd33} = file:open(File3 ++ ".3", [read]), - ?line {ok,"this is a head message three"} = file:read(Fd33, 200), - ?line ok = file:close(Fd31), - ?line ok = file:close(Fd32), - ?line ok = file:close(Fd33), - - ?line ok = disk_log:close(a), - ?line ok = disk_log:close(b), - ?line ok = disk_log:close(c), - ?line ok = file:delete(File1), + {ok, c} = disk_log:open([{name, c}, {type, wrap}, {size, {100,3}}, + {format,external}, {head,"this is a head "}, + {file, File3}]), + ok = disk_log:blog(c, "message one"), + ok = disk_log:inc_wrap_file(c), + ok = disk_log:blog(c, "message two"), + ok = disk_log:inc_wrap_file(c), + ok = disk_log:blog(c, "message three"), + ok = disk_log:inc_wrap_file(c), + ok = disk_log:blog(c, "message four"), + ok = disk_log:sync(c), + {ok, Fd31} = file:open(File3 ++ ".1", [read]), + {ok,"this is a head message four"} = file:read(Fd31, 200), + {ok, Fd32} = file:open(File3 ++ ".2", [read]), + {ok,"this is a head message two"} = file:read(Fd32, 200), + {ok, Fd33} = file:open(File3 ++ ".3", [read]), + {ok,"this is a head message three"} = file:read(Fd33, 200), + ok = file:close(Fd31), + ok = file:close(Fd32), + ok = file:close(Fd33), + + ok = disk_log:close(a), + ok = disk_log:close(b), + ok = disk_log:close(c), + ok = file:delete(File1), del(File2, 3), del(File3, 3). -halt_ext_inf(suite) -> []; -halt_ext_inf(doc) -> ["Test halt disk log, external, infinity"]; +%% Test halt disk log, external, infinity. halt_ext_inf(Conf) when is_list(Conf) -> Dir = ?privdir(Conf), File = filename:join(Dir, "a.LOG"), - ?line {ok, a} = disk_log:open([{name,a}, {type,halt}, {size,infinity}, - {format,external}, - {file, File}]), - ?line xsimple_log(File, a), - ?line ok = disk_log:close(a), - ?line ok = file:delete(File). + {ok, a} = disk_log:open([{name,a}, {type,halt}, {size,infinity}, + {format,external}, + {file, File}]), + xsimple_log(File, a), + ok = disk_log:close(a), + ok = file:delete(File). -halt_ext_sz_1(suite) -> []; -halt_ext_sz_1(doc) -> ["Test halt disk log, external, size defined"]; +%% Test halt disk log, external, size defined. halt_ext_sz_1(Conf) when is_list(Conf) -> Dir = ?privdir(Conf), File = filename:join(Dir, "a.LOG"), - ?line {ok, a} = disk_log:open([{name,a}, {type,halt}, {size,18000}, - {format,external}, - {file, File}]), + {ok, a} = disk_log:open([{name,a}, {type,halt}, {size,18000}, + {format,external}, + {file, File}]), xsimple_log(File, a), - ?line ok = disk_log:truncate(a), - ?line [] = get_list(File, a), + ok = disk_log:truncate(a), + [] = get_list(File, a), {B1, T1} = x_mk_bytes(10000), {B2, T2} = x_mk_bytes(5000), {B3, T3} = x_mk_bytes(1000), - ?line ok = disk_log:blog(a, B1), - ?line case get_list(File, a) of - T1 -> - ok; - E1 -> - test_server_fail({bad_terms, E1, T1}) - end, - ?line ok = disk_log:blog(a, B2), - ?line {error, {full, a}} = disk_log:blog_terms(a, [B3,B3,B1]), - ?line ok = disk_log:balog(a, B1), - ?line Tmp = T1 ++ T2 ++ T3 ++ T3, - ?line case get_list(File, a) of - Tmp -> - ok; - E2 -> - test_server_fail({bad_terms, E2, Tmp}) - end, - ?line ok = disk_log:close(a), - ?line ok = file:delete(File). + ok = disk_log:blog(a, B1), + case get_list(File, a) of + T1 -> + ok; + E1 -> + test_server_fail({bad_terms, E1, T1}) + end, + ok = disk_log:blog(a, B2), + {error, {full, a}} = disk_log:blog_terms(a, [B3,B3,B1]), + ok = disk_log:balog(a, B1), + Tmp = T1 ++ T2 ++ T3 ++ T3, + case get_list(File, a) of + Tmp -> + ok; + E2 -> + test_server_fail({bad_terms, E2, Tmp}) + end, + ok = disk_log:close(a), + ok = file:delete(File). -halt_ext_sz_2(suite) -> []; -halt_ext_sz_2(doc) -> ["Test halt disk log, external, size defined"]; +%% Test halt disk log, external, size defined. halt_ext_sz_2(Conf) when is_list(Conf) -> Dir = ?privdir(Conf), File1 = filename:join(Dir, "a.LOG"), File2 = filename:join(Dir, "b.LOG"), File3 = filename:join(Dir, "c.LOG"), - ?line {ok, a} = disk_log:open([{name,a}, {type,halt}, {size,8191}, - {format,external}, - {file, File1}]), - ?line {ok, b} = disk_log:open([{name,b}, {type,halt}, {size,8192}, - {format,external}, - {file, File2}]), - ?line {ok, c} = disk_log:open([{name,c}, {type,halt}, {size,8193}, - {format,external}, - {file, File3}]), + {ok, a} = disk_log:open([{name,a}, {type,halt}, {size,8191}, + {format,external}, + {file, File1}]), + {ok, b} = disk_log:open([{name,b}, {type,halt}, {size,8192}, + {format,external}, + {file, File2}]), + {ok, c} = disk_log:open([{name,c}, {type,halt}, {size,8193}, + {format,external}, + {file, File3}]), {B1, T1} = x_mk_bytes(8191), {B2, T2} = x_mk_bytes(8192), {B3, T3} = x_mk_bytes(8193), - ?line ok = disk_log:blog(a, B1), - ?line ok = disk_log:blog(b, B2), - ?line ok = disk_log:blog(c, B3), - ?line case get_list(File1, a) of - T1 -> - ok; - E1 -> - test_server_fail({bad_terms, E1, T1}) - end, - ?line case get_list(File2, b) of - T2 -> - ok; - E2 -> - test_server_fail({bad_terms, E2, T2}) - end, - ?line case get_list(File3, c) of - T3 -> - ok; - E3 -> - test_server_fail({bad_terms, E3, T3}) - end, - ?line ok = disk_log:truncate(a), - ?line ok = disk_log:truncate(b), - ?line {error, {full, a}} = disk_log:blog(a, B2), - ?line Error1 = {error, {full, b}} = disk_log:blog(b, B3), - ?line "The halt log" ++ _ = format_error(Error1), - ?line true = info(b, full, false), - ?line [] = get_list(File1, a), - ?line [] = get_list(File2, b), - ?line ok = disk_log:close(a), - ?line ok = disk_log:close(b), - ?line ok = disk_log:close(c), - ?line ok = file:delete(File1), - ?line ok = file:delete(File2), - ?line ok = file:delete(File3), + ok = disk_log:blog(a, B1), + ok = disk_log:blog(b, B2), + ok = disk_log:blog(c, B3), + case get_list(File1, a) of + T1 -> + ok; + E1 -> + test_server_fail({bad_terms, E1, T1}) + end, + case get_list(File2, b) of + T2 -> + ok; + E2 -> + test_server_fail({bad_terms, E2, T2}) + end, + case get_list(File3, c) of + T3 -> + ok; + E3 -> + test_server_fail({bad_terms, E3, T3}) + end, + ok = disk_log:truncate(a), + ok = disk_log:truncate(b), + {error, {full, a}} = disk_log:blog(a, B2), + Error1 = {error, {full, b}} = disk_log:blog(b, B3), + "The halt log" ++ _ = format_error(Error1), + true = info(b, full, false), + [] = get_list(File1, a), + [] = get_list(File2, b), + ok = disk_log:close(a), + ok = disk_log:close(b), + ok = disk_log:close(c), + ok = file:delete(File1), + ok = file:delete(File2), + ok = file:delete(File3), ok. -wrap_ext_1(suite) -> []; -wrap_ext_1(doc) -> ["Test wrap disk log, external, size defined"]; +%% Test wrap disk log, external, size defined. wrap_ext_1(Conf) when is_list(Conf) -> Dir = ?privdir(Conf), File = filename:join(Dir, "a.LOG"), - ?line {ok, a} = disk_log:open([{name,a}, {type,wrap}, {size,{8000, 4}}, - {format,external}, - {file, File}]), + {ok, a} = disk_log:open([{name,a}, {type,wrap}, {size,{8000, 4}}, + {format,external}, + {file, File}]), x2simple_log(File ++ ".1", a), - ?line ok = disk_log:close(a), -% del(File, 4), - ?line {ok, a} = disk_log:open([{name,a}, {type,wrap}, {size,{8000, 4}}, - {format,external}, - {file, File}]), + ok = disk_log:close(a), + %% del(File, 4), + {ok, a} = disk_log:open([{name,a}, {type,wrap}, {size,{8000, 4}}, + {format,external}, + {file, File}]), {B1, _T1} = x_mk_bytes(10000), % file 2 {B2, T2} = x_mk_bytes(5000), % file 3 {B3, T3} = x_mk_bytes(4000), % file 4 {B4, T4} = x_mk_bytes(2000), % file 4 {B5, T5} = x_mk_bytes(5000), % file 1 {B6, T6} = x_mk_bytes(5000), % file 2 - ?line ok = disk_log:blog(a, B1), - ?line ok = disk_log:blog(a, B2), - ?line ok = disk_log:blog(a, B3), - ?line ok = disk_log:blog_terms(a, [B4, B5, B6]), - ?line case get_list(File ++ ".3", a) of - T2 -> - ok; - E2 -> - test_server_fail({bad_terms, E2, T2}) - end, - ?line T34 = T3 ++ T4, - ?line case get_list(File ++ ".4", a) of - T34 -> - ok; - E34 -> - test_server_fail({bad_terms, E34, T34}) - end, - ?line case get_list(File ++ ".1", a) of - T5 -> - ok; - E5 -> - test_server_fail({bad_terms, E5, T5}) - end, - ?line case get_list(File ++ ".2", a) of - T6 -> - ok; - E6 -> - test_server_fail({bad_terms, E6, T6}) - end, - ?line ok = disk_log:close(a), + ok = disk_log:blog(a, B1), + ok = disk_log:blog(a, B2), + ok = disk_log:blog(a, B3), + ok = disk_log:blog_terms(a, [B4, B5, B6]), + case get_list(File ++ ".3", a) of + T2 -> + ok; + E2 -> + test_server_fail({bad_terms, E2, T2}) + end, + T34 = T3 ++ T4, + case get_list(File ++ ".4", a) of + T34 -> + ok; + E34 -> + test_server_fail({bad_terms, E34, T34}) + end, + case get_list(File ++ ".1", a) of + T5 -> + ok; + E5 -> + test_server_fail({bad_terms, E5, T5}) + end, + case get_list(File ++ ".2", a) of + T6 -> + ok; + E6 -> + test_server_fail({bad_terms, E6, T6}) + end, + ok = disk_log:close(a), del(File, 4). -wrap_ext_2(suite) -> []; -wrap_ext_2(doc) -> ["Test wrap disk log, external, size defined"]; +%% Test wrap disk log, external, size defined. wrap_ext_2(Conf) when is_list(Conf) -> Dir = ?privdir(Conf), File1 = filename:join(Dir, "a.LOG"), File2 = filename:join(Dir, "b.LOG"), File3 = filename:join(Dir, "c.LOG"), - ?line {ok, a} = disk_log:open([{name,a}, {type,wrap}, {size,{8191,3}}, - {format,external}, - {file, File1}]), - ?line {ok, b} = disk_log:open([{name,b}, {type,wrap}, {size,{8192,3}}, - {format,external}, - {file, File2}]), - ?line {ok, c} = disk_log:open([{name,c}, {type,wrap}, {size,{8193,3}}, - {format,external}, - {file, File3}]), + {ok, a} = disk_log:open([{name,a}, {type,wrap}, {size,{8191,3}}, + {format,external}, + {file, File1}]), + {ok, b} = disk_log:open([{name,b}, {type,wrap}, {size,{8192,3}}, + {format,external}, + {file, File2}]), + {ok, c} = disk_log:open([{name,c}, {type,wrap}, {size,{8193,3}}, + {format,external}, + {file, File3}]), {B1, T1} = x_mk_bytes(8191), {B2, T2} = x_mk_bytes(8192), {B3, T3} = x_mk_bytes(8193), - ?line ok = disk_log:blog(a, B1), - ?line ok = disk_log:blog(b, B2), - ?line ok = disk_log:blog(c, B3), - ?line case get_list(File1 ++ ".1", a) of - T1 -> - ok; - E1 -> - test_server_fail({bad_terms, E1, T1}) - end, - ?line case get_list(File2 ++ ".1", b) of - T2 -> - ok; - E2 -> - test_server_fail({bad_terms, E2, T2}) - end, - ?line case get_list(File3 ++ ".1", c) of - T3 -> - ok; - E3 -> - test_server_fail({bad_terms, E3, T3}) - end, - ?line ok = disk_log:close(a), - ?line ok = disk_log:close(b), - ?line ok = disk_log:close(c), - ?line del(File1, 3), - ?line del(File2, 3), - ?line del(File3, 3), + ok = disk_log:blog(a, B1), + ok = disk_log:blog(b, B2), + ok = disk_log:blog(c, B3), + case get_list(File1 ++ ".1", a) of + T1 -> + ok; + E1 -> + test_server_fail({bad_terms, E1, T1}) + end, + case get_list(File2 ++ ".1", b) of + T2 -> + ok; + E2 -> + test_server_fail({bad_terms, E2, T2}) + end, + case get_list(File3 ++ ".1", c) of + T3 -> + ok; + E3 -> + test_server_fail({bad_terms, E3, T3}) + end, + ok = disk_log:close(a), + ok = disk_log:close(b), + ok = disk_log:close(c), + del(File1, 3), + del(File2, 3), + del(File3, 3), ok. simple_log(Log) -> @@ -878,61 +837,61 @@ simple_log(Log) -> T2 = hopp, T3 = {tjena, 12}, T4 = mk_bytes(10000), - ?line ok = disk_log:log(Log, T1), - ?line ok = disk_log:log_terms(Log, [T2, T3]), - ?line case get_all_terms(Log) of - [T1, T2, T3] -> - ok; - E1 -> - test_server_fail({bad_terms, E1, [T1, T2, T3]}) - end, - ?line ok = disk_log:log(a, T4), - ?line case get_all_terms(Log) of - [T1, T2, T3, T4] -> - ok; - E2 -> - test_server_fail({bad_terms, E2, [T1, T2, T3, T4]}) - end. + ok = disk_log:log(Log, T1), + ok = disk_log:log_terms(Log, [T2, T3]), + case get_all_terms(Log) of + [T1, T2, T3] -> + ok; + E1 -> + test_server_fail({bad_terms, E1, [T1, T2, T3]}) + end, + ok = disk_log:log(a, T4), + case get_all_terms(Log) of + [T1, T2, T3, T4] -> + ok; + E2 -> + test_server_fail({bad_terms, E2, [T1, T2, T3, T4]}) + end. xsimple_log(File, Log) -> T1 = "hej", T2 = list_to_binary("hopp"), T3 = list_to_binary(["sena", list_to_binary("sejer")]), T4 = list_to_binary(By = mk_bytes(10000)), - ?line ok = disk_log:blog(Log, T1), - ?line ok = disk_log:blog_terms(Log, [T2, T3]), - ?line X = "hejhoppsenasejer", - ?line X2 = get_list(File, Log), - ?line case X2 of - X -> ok; - Z1 -> test_server_fail({bad_terms, Z1, X2}) - end, - ?line ok = disk_log:blog(Log, T4), - ?line Tmp = get_list(File, Log), - ?line case X ++ By of - Tmp -> ok; - Z2 -> test_server_fail({bad_terms, Z2, X ++ By}) - end. + ok = disk_log:blog(Log, T1), + ok = disk_log:blog_terms(Log, [T2, T3]), + X = "hejhoppsenasejer", + X2 = get_list(File, Log), + case X2 of + X -> ok; + Z1 -> test_server_fail({bad_terms, Z1, X2}) + end, + ok = disk_log:blog(Log, T4), + Tmp = get_list(File, Log), + case X ++ By of + Tmp -> ok; + Z2 -> test_server_fail({bad_terms, Z2, X ++ By}) + end. x2simple_log(File, Log) -> T1 = "hej", T2 = list_to_binary("hopp"), T3 = list_to_binary(["sena", list_to_binary("sejer")]), T4 = list_to_binary(By = mk_bytes(1000)), - ?line ok = disk_log:blog(Log, T1), - ?line ok = disk_log:blog_terms(Log, [T2, T3]), - ?line X = "hejhoppsenasejer", - ?line X2 = get_list(File, Log), - ?line case X2 of - X -> ok; - Z1 -> test_server_fail({bad_terms, Z1, X2}) - end, - ?line ok = disk_log:blog(Log, T4), - ?line Tmp = get_list(File, Log), - ?line case X ++ By of - Tmp -> ok; - Z2 -> test_server_fail({bad_terms, Z2, X ++ By}) - end. + ok = disk_log:blog(Log, T1), + ok = disk_log:blog_terms(Log, [T2, T3]), + X = "hejhoppsenasejer", + X2 = get_list(File, Log), + case X2 of + X -> ok; + Z1 -> test_server_fail({bad_terms, Z1, X2}) + end, + ok = disk_log:blog(Log, T4), + Tmp = get_list(File, Log), + case X ++ By of + Tmp -> ok; + Z2 -> test_server_fail({bad_terms, Z2, X ++ By}) + end. x_mk_bytes(N) -> X = lists:duplicate(N, $a), @@ -946,7 +905,7 @@ mk_bytes(N) when N > 4 -> end. get_list(File, Log) -> - ?t:format(0, "File ~p~n",[File]), + ct:pal(?HI_VERBOSITY, "File ~p~n", [File]), ok = disk_log:sync(Log), {ok, B} = file:read_file(File), binary_to_list(B). @@ -954,7 +913,7 @@ get_list(File, Log) -> get_all_terms(Log, File, Type) -> {ok, _Log} = disk_log:open([{name,Log}, {type,Type}, {size,infinity}, - {format,internal}, {file, File}, + {format,internal}, {file, File}, {mode, read_only}]), Ts = get_all_terms(Log), ok = disk_log:close(Log), @@ -975,14 +934,14 @@ get_all_terms1(Log, Cont, Res) -> get_all_terms_and_bad(Log, File, Type) -> {ok, _Log} = disk_log:open([{name,Log}, {type,Type}, {size,infinity}, - {format,internal}, {file, File}, + {format,internal}, {file, File}, {mode, read_only}]), Ts = get_all_terms_and_bad(Log), ok = disk_log:close(Log), Ts. get_all_terms_and_bad(Log) -> - ?line read_only = info(Log, mode, foo), + read_only = info(Log, mode, foo), get_all_terms_and_bad1(Log, start, [], 0). %% @@ -998,7 +957,7 @@ get_all_terms_and_bad1(Log, Cont, Res, Bad0) -> get_all_binary_terms_and_bad(Log, File, Type) -> {ok, _Log} = disk_log:open([{name,Log}, {type,Type}, {size,infinity}, - {format,internal}, {file, File}, + {format,internal}, {file, File}, {mode, read_only}]), Ts = get_all_binary_terms_and_bad(Log), ok = disk_log:close(Log), @@ -1036,7 +995,7 @@ xx() -> {format,internal}, {file, File}]), W = xwr(a, 400), disk_log:close(a), -% file:delete(File), + %% file:delete(File), W. %% old: 6150 @@ -1185,73 +1144,72 @@ end_times({T1,W1}) -> {T2-T1, W2-W1}. -head_func(suite) -> []; -head_func(doc) -> ["Test head parameter"]; +%% Test head parameter. head_func(Conf) when is_list(Conf) -> Dir = ?privdir(Conf), File = filename:join(Dir, "a.LOG"), ets:new(xxx, [named_table, set, public]), ets:insert(xxx, {wrapc, 0}), - ?line {ok, a} = disk_log:open([{name,a}, {file, File}, {type, wrap}, - {size, {100,4}}, - {head_func, {?MODULE, hf, []}}]), - ?line B = mk_bytes(60), - ?line disk_log:log(a, B), - ?line disk_log:alog(a, B), - ?line disk_log:alog(a, B), - ?line disk_log:log(a, B), + {ok, a} = disk_log:open([{name,a}, {file, File}, {type, wrap}, + {size, {100,4}}, + {head_func, {?MODULE, hf, []}}]), + B = mk_bytes(60), + disk_log:log(a, B), + disk_log:alog(a, B), + disk_log:alog(a, B), + disk_log:log(a, B), H = [1,2,3], - ?line [{wrapc, 4}] = ets:lookup(xxx, wrapc), + [{wrapc, 4}] = ets:lookup(xxx, wrapc), ets:delete(xxx), - ?line case get_all_terms(a) of - [H,B,H,B,H,B,H,B] -> - ok; - E1 -> - test_server_fail({bad_terms, E1, - [H,B,H,B,H,B,H,B]}) - end, - ?line 8 = no_written_items(a), + case get_all_terms(a) of + [H,B,H,B,H,B,H,B] -> + ok; + E1 -> + test_server_fail({bad_terms, E1, + [H,B,H,B,H,B,H,B]}) + end, + 8 = no_written_items(a), disk_log:close(a), del(File, 4), - % invalid header function - ?line {error, {invalid_header, {_, {term}}}} = + %% invalid header function + {error, {invalid_header, {_, {term}}}} = disk_log:open([{name, n}, {file, File}, {type, halt}, {format, external}, {head_func, {?MODULE, head_fun, [{term}]}}]), file:delete(File), - ?line {error, {invalid_header, _}} = + {error, {invalid_header, _}} = disk_log:open([{name, n}, {file, File}, {type, halt}, {format, external}, {head_func, {?MODULE, head_fun, [{ok,{term}}]}}]), file:delete(File), - ?line {ok,n} = + {ok,n} = disk_log:open([{name, n}, {file, File}, {type, halt}, {format, external}, {head_func, {?MODULE, head_fun, [{ok,<<"head">>}]}}]), - ?line ok = disk_log:close(n), - ?line {ok,<<"head">>} = file:read_file(File), + ok = disk_log:close(n), + {ok,<<"head">>} = file:read_file(File), file:delete(File), - ?line {ok,n} = + {ok,n} = disk_log:open([{name, n}, {file, File}, {type, halt}, {format, external}, {head_func, {?MODULE, head_fun, [{ok,"head"}]}}]), - ?line ok = disk_log:close(n), - ?line {ok,<<"head">>} = file:read_file(File), + ok = disk_log:close(n), + {ok,<<"head">>} = file:read_file(File), file:delete(File), - ?line Error1 = {error, {badarg, _}} = + Error1 = {error, {badarg, _}} = disk_log:open([{name, n}, {file, File}, {type, wrap}, {head_func, {tjo,hej,san}},{size, {100, 4}}]), - ?line "The argument " ++ _ = format_error(Error1), - - ?line Error2 = {error, {invalid_header, _}} = + "The argument " ++ _ = format_error(Error1), + + Error2 = {error, {invalid_header, _}} = disk_log:open([{name, n}, {file, File}, {type, halt}, {head_func, {tjo,hej,[san]}}]), - ?line "The disk log header" ++ _ = format_error(Error2), + "The disk log header" ++ _ = format_error(Error2), file:delete(File). @@ -1262,194 +1220,186 @@ hf() -> ets:update_counter(xxx, wrapc, 1), {ok, [1,2,3]}. -plain_head(suite) -> []; -plain_head(doc) -> ["Test head parameter"]; +%% Test head parameter. plain_head(Conf) when is_list(Conf) -> Dir = ?privdir(Conf), File = filename:join(Dir, "a.LOG"), H = [1,2,3], - ?line {ok, a} = disk_log:open([{name,a}, {file, File}, {type, wrap}, - {size, {100,4}}, {head, H}]), + {ok, a} = disk_log:open([{name,a}, {file, File}, {type, wrap}, + {size, {100,4}}, {head, H}]), %% This one is not "counted". - ?line {ok, a} = disk_log:open([{name,a}, {file, File}, {type, wrap}, - {size, {100,4}}, {head, H}]), - ?line B = mk_bytes(60), - ?line disk_log:log(a, B), - ?line disk_log:alog(a, B), - ?line disk_log:alog(a, B), - ?line disk_log:log(a, B), - ?line case get_all_terms(a) of - [H,B,H,B,H,B,H,B] -> - ok; - E1 -> - test_server_fail({bad_terms, E1, - [H,B,H,B,H,B,H,B]}) - end, - ?line 8 = no_written_items(a), - ?line ok = disk_log:close(a), - ?line {error, no_such_log} = disk_log:close(a), + {ok, a} = disk_log:open([{name,a}, {file, File}, {type, wrap}, + {size, {100,4}}, {head, H}]), + B = mk_bytes(60), + disk_log:log(a, B), + disk_log:alog(a, B), + disk_log:alog(a, B), + disk_log:log(a, B), + case get_all_terms(a) of + [H,B,H,B,H,B,H,B] -> + ok; + E1 -> + test_server_fail({bad_terms, E1, + [H,B,H,B,H,B,H,B]}) + end, + 8 = no_written_items(a), + ok = disk_log:close(a), + {error, no_such_log} = disk_log:close(a), del(File, 4). -one_header(suite) -> []; -one_header(doc) -> ["Test that a header is just printed once in a log file"]; +%% Test that a header is just printed once in a log file. one_header(Conf) when is_list(Conf) -> Dir = ?privdir(Conf), File = filename:join(Dir, "a.LOG"), H = [1,2,3], - ?line {ok, a} = disk_log:open([{name,a}, {file, File}, {type, wrap}, - {size, {100,4}}, {head, H}]), - ?line B = mk_bytes(60), - ?line ok = disk_log:log(a, B), - ?line ok = disk_log:alog(a, B), - ?line ok = disk_log:alog(a, B), - ?line ok = disk_log:log(a, B), - ?line case get_all_terms(a) of - [H,B,H,B,H,B,H,B] -> - ok; - E1 -> - test_server_fail({bad_terms, E1, - [H,B,H,B,H,B,H,B]}) - end, - ?line 8 = no_written_items(a), - ?line ok = disk_log:close(a), + {ok, a} = disk_log:open([{name,a}, {file, File}, {type, wrap}, + {size, {100,4}}, {head, H}]), + B = mk_bytes(60), + ok = disk_log:log(a, B), + ok = disk_log:alog(a, B), + ok = disk_log:alog(a, B), + ok = disk_log:log(a, B), + case get_all_terms(a) of + [H,B,H,B,H,B,H,B] -> + ok; + E1 -> + test_server_fail({bad_terms, E1, + [H,B,H,B,H,B,H,B]}) + end, + 8 = no_written_items(a), + ok = disk_log:close(a), del(File, 4), Fileb = filename:join(Dir, "b.LOG"), - ?line {ok, b} = disk_log:open([{name,b}, {file, Fileb}, {head, H}]), - ?line ok = disk_log:close(b), - ?line {ok, b} = disk_log:open([{name,b}, {file, Fileb}, {head, H}]), - ?line ok = disk_log:log(b, "first log"), - ?line ok = disk_log:alog(b, "second log"), - ?line ok = disk_log:close(b), - ?line {ok, b} = disk_log:open([{name,b}, {file, Fileb}, {head, H}]), - ?line ok = disk_log:alog(b, "3rd log"), - ?line ok = disk_log:log(b, "4th log"), - ?line case get_all_terms(b) of - [H, "first log", "second log", "3rd log", "4th log"] -> - ok; - E2 -> - test_server_fail({bad_terms, E2, - [H, "first log", "second log", - "3rd log", "4th log"]}) - end, - ?line 2 = no_written_items(b), - ?line ok = disk_log:close(b), - ?line ok = file:delete(Fileb), + {ok, b} = disk_log:open([{name,b}, {file, Fileb}, {head, H}]), + ok = disk_log:close(b), + {ok, b} = disk_log:open([{name,b}, {file, Fileb}, {head, H}]), + ok = disk_log:log(b, "first log"), + ok = disk_log:alog(b, "second log"), + ok = disk_log:close(b), + {ok, b} = disk_log:open([{name,b}, {file, Fileb}, {head, H}]), + ok = disk_log:alog(b, "3rd log"), + ok = disk_log:log(b, "4th log"), + case get_all_terms(b) of + [H, "first log", "second log", "3rd log", "4th log"] -> + ok; + E2 -> + test_server_fail({bad_terms, E2, + [H, "first log", "second log", + "3rd log", "4th log"]}) + end, + 2 = no_written_items(b), + ok = disk_log:close(b), + ok = file:delete(Fileb), Filec = filename:join(Dir, "c.LOG"), H2 = "this is a header ", - ?line {ok, c} = disk_log:open([{name,c}, {format, external}, - {file, Filec}, {head, H2}]), - ?line ok = disk_log:close(c), - ?line {ok, c} = disk_log:open([{name,c}, {format, external}, - {file, Filec}, {head, H2}]), - ?line ok = disk_log:blog(c, "first log"), - ?line ok = disk_log:balog(c, "second log"), - ?line ok = disk_log:close(c), - ?line {ok, c} = disk_log:open([{name,c}, {format, external}, - {file, Filec}, {head, H2}]), - ?line ok = disk_log:balog(c, "3rd log"), - ?line ok = disk_log:blog(c, "4th log"), - ?line ok = disk_log:sync(c), - ?line {ok, Fdc} = file:open(Filec, [read]), - ?line {ok,"this is a header first logsecond log3rd log4th log"} = + {ok, c} = disk_log:open([{name,c}, {format, external}, + {file, Filec}, {head, H2}]), + ok = disk_log:close(c), + {ok, c} = disk_log:open([{name,c}, {format, external}, + {file, Filec}, {head, H2}]), + ok = disk_log:blog(c, "first log"), + ok = disk_log:balog(c, "second log"), + ok = disk_log:close(c), + {ok, c} = disk_log:open([{name,c}, {format, external}, + {file, Filec}, {head, H2}]), + ok = disk_log:balog(c, "3rd log"), + ok = disk_log:blog(c, "4th log"), + ok = disk_log:sync(c), + {ok, Fdc} = file:open(Filec, [read]), + {ok,"this is a header first logsecond log3rd log4th log"} = file:read(Fdc, 200), - ?line ok = file:close(Fdc), - ?line 2 = no_written_items(c), - ?line disk_log:close(c), - ?line ok = file:delete(Filec), + ok = file:close(Fdc), + 2 = no_written_items(c), + disk_log:close(c), + ok = file:delete(Filec), ok. -wrap_notif(suite) -> []; -wrap_notif(doc) -> ["Test notify parameter, wrap"]; +%% Test notify parameter, wrap. wrap_notif(Conf) when is_list(Conf) -> Dir = ?privdir(Conf), File = filename:join(Dir, "a.LOG"), - ?line {ok, a} = disk_log:open([{name,a}, {file, File}, {type, wrap}, - {size, {100,4}}, {notify, true}]), - ?line B = mk_bytes(60), - ?line disk_log:log(a, B), - ?line disk_log:alog(a, B), - ?line disk_log:alog(a, B), - ?line disk_log:log(a, B), - ?line disk_log:log(a, B), - ?line rec(3, {disk_log, node(), a, {wrap, 0}}), - ?line rec(1, {disk_log, node(), a, {wrap, 1}}), + {ok, a} = disk_log:open([{name,a}, {file, File}, {type, wrap}, + {size, {100,4}}, {notify, true}]), + B = mk_bytes(60), + disk_log:log(a, B), + disk_log:alog(a, B), + disk_log:alog(a, B), + disk_log:log(a, B), + disk_log:log(a, B), + rec(3, {disk_log, node(), a, {wrap, 0}}), + rec(1, {disk_log, node(), a, {wrap, 1}}), disk_log:close(a), del(File, 4). -full_notif(suite) -> []; -full_notif(doc) -> ["Test notify parameter, wrap, filled file"]; +%% Test notify parameter, wrap, filled file. full_notif(Conf) when is_list(Conf) -> Dir = ?privdir(Conf), File = filename:join(Dir, "a.LOG"), file:delete(File), - ?line {ok, a} = disk_log:open([{name, a}, {file, File}, {type, halt}, - {size, 100}, {notify, true}]), - ?line B = mk_bytes(60), - ?line disk_log:log(a, B), - ?line disk_log:alog(a, B), - ?line rec(1, {disk_log, node(), a, full}), + {ok, a} = disk_log:open([{name, a}, {file, File}, {type, halt}, + {size, 100}, {notify, true}]), + B = mk_bytes(60), + disk_log:log(a, B), + disk_log:alog(a, B), + rec(1, {disk_log, node(), a, full}), disk_log:close(a), file:delete(File). -trunc_notif(suite) -> []; -trunc_notif(doc) -> ["Test notify parameter, wrap, truncated file"]; +%% Test notify parameter, wrap, truncated file. trunc_notif(Conf) when is_list(Conf) -> Dir = ?privdir(Conf), File = filename:join(Dir, "a.LOG"), File2 = filename:join(Dir, "a.DUMP"), - ?line {ok, a} = disk_log:open([{name, a}, {file, File}, {type, halt}, - {size, 100}, {notify, true}]), - ?line B = mk_bytes(60), - ?line disk_log:log(a, B), - ?line disk_log:truncate(a), - ?line rec(1, {disk_log, node(), a, {truncated, 1}}), - ?line disk_log:log(a, B), - ?line ok = disk_log:reopen(a, File2), - ?line rec(1, {disk_log, node(), a, {truncated, 1}}), + {ok, a} = disk_log:open([{name, a}, {file, File}, {type, halt}, + {size, 100}, {notify, true}]), + B = mk_bytes(60), + disk_log:log(a, B), + disk_log:truncate(a), + rec(1, {disk_log, node(), a, {truncated, 1}}), + disk_log:log(a, B), + ok = disk_log:reopen(a, File2), + rec(1, {disk_log, node(), a, {truncated, 1}}), disk_log:close(a), file:delete(File), file:delete(File2). -blocked_notif(suite) -> []; -blocked_notif(doc) -> - ["Test notify parameters 'format_external' and 'blocked_log"]; +%% Test notify parameters 'format_external' and 'blocked_log. blocked_notif(Conf) when is_list(Conf) -> Dir = ?privdir(Conf), File = filename:join(Dir, "n.LOG"), No = 4, - ?line {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, - {size, {100,No}}, {notify, true}, - {format, external}]), - ?line B = mk_bytes(60), - ?line Error1 = {error,{format_external,n}} = disk_log:log(n, B), - ?line "The requested operation" ++ _ = format_error(Error1), - ?line ok = disk_log:blog(n, B), - ?line ok = disk_log:alog(n, B), - ?line rec(1, {disk_log, node(), n, {format_external, term_to_binary(B)}}), - ?line ok = disk_log:alog_terms(n, [B,B,B,B]), - ?line rec(1, {disk_log, node(), n, {format_external, - lists:map(fun term_to_binary/1, [B,B,B,B])}}), - ?line ok = disk_log:block(n, false), - ?line ok = disk_log:alog(n, B), - ?line rec(1, {disk_log, node(), n, {blocked_log, term_to_binary(B)}}), - ?line ok = disk_log:balog(n, B), - ?line rec(1, {disk_log, node(), n, {blocked_log, list_to_binary(B)}}), - ?line ok = disk_log:balog_terms(n, [B,B,B,B]), - ?line disk_log:close(n), - ?line rec(1, {disk_log, node(), n, {blocked_log, - lists:map(fun list_to_binary/1, [B,B,B,B])}}), - ?line del(File, No). - - -new_idx_vsn(suite) -> []; -new_idx_vsn(doc) -> ["Test the new version of the .idx file"]; + {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, + {size, {100,No}}, {notify, true}, + {format, external}]), + B = mk_bytes(60), + Error1 = {error,{format_external,n}} = disk_log:log(n, B), + "The requested operation" ++ _ = format_error(Error1), + ok = disk_log:blog(n, B), + ok = disk_log:alog(n, B), + rec(1, {disk_log, node(), n, {format_external, [term_to_binary(B)]}}), + ok = disk_log:alog_terms(n, [B,B,B,B]), + rec(1, {disk_log, node(), n, {format_external, + lists:map(fun term_to_binary/1, [B,B,B,B])}}), + ok = disk_log:block(n, false), + ok = disk_log:alog(n, B), + rec(1, {disk_log, node(), n, {blocked_log, [term_to_binary(B)]}}), + ok = disk_log:balog(n, B), + rec(1, {disk_log, node(), n, {blocked_log, [list_to_binary(B)]}}), + ok = disk_log:balog_terms(n, [B,B,B,B]), + disk_log:close(n), + rec(1, {disk_log, node(), n, {blocked_log, + lists:map(fun list_to_binary/1, [B,B,B,B])}}), + del(File, No). + + +%% Test the new version of the .idx file. new_idx_vsn(Conf) when is_list(Conf) -> DataDir = ?datadir(Conf), PrivDir = ?privdir(Conf), @@ -1458,201 +1408,197 @@ new_idx_vsn(Conf) when is_list(Conf) -> Kurt2 = filename:join(PrivDir, "kurt2.LOG"), %% Test that a wrap log file can have more than 255 files - ?line {ok, new_vsn} = disk_log:open([{file, File}, {name, new_vsn}, - {type, wrap}, {size, {40, 270}}]), - ?line ok = log(new_vsn, 280), - ?line {ok, Bin} = file:read_file(add_ext(File, "idx")), - ?line <<0,0:32,2,10:32,1:64,1:64,_/binary>> = Bin, - ?line disk_log:close(new_vsn), - ?line del(File, 270), + {ok, new_vsn} = disk_log:open([{file, File}, {name, new_vsn}, + {type, wrap}, {size, {40, 270}}]), + ok = log(new_vsn, 280), + {ok, Bin} = file:read_file(add_ext(File, "idx")), + <<0,0:32,2,10:32,1:64,1:64,_/binary>> = Bin, + disk_log:close(new_vsn), + del(File, 270), %% convert a very old version (0) of wrap log file to the new format (2) copy_wrap_log("kurt.LOG", 4, DataDir, PrivDir), - ?line {repaired, kurt, {recovered, 1}, {badbytes, 0}} = + {repaired, kurt, {recovered, 1}, {badbytes, 0}} = disk_log:open([{file, Kurt}, {name, kurt}, {type, wrap}, {size, {40, 4}}]), - ?line ok = disk_log:log(kurt, "this is a logged message number X"), - ?line ok = disk_log:log(kurt, "this is a logged message number Y"), - ?line {ok, BinK} = file:read_file(add_ext(Kurt, "idx")), - ?line <<0,0:32,2,2:32,1:64,1:64,1:64,1:64>> = BinK, - ?line {{40,4}, 2} = disk_log_1:read_size_file_version(Kurt), + ok = disk_log:log(kurt, "this is a logged message number X"), + ok = disk_log:log(kurt, "this is a logged message number Y"), + {ok, BinK} = file:read_file(add_ext(Kurt, "idx")), + <<0,0:32,2,2:32,1:64,1:64,1:64,1:64>> = BinK, + {{40,4}, 2} = disk_log_1:read_size_file_version(Kurt), disk_log:close(kurt), - ?line del(Kurt, 4), + del(Kurt, 4), %% keep the old format (1) copy_wrap_log("kurt2.LOG", 4, DataDir, PrivDir), - ?line {repaired, kurt2, {recovered, 1}, {badbytes, 0}} = + {repaired, kurt2, {recovered, 1}, {badbytes, 0}} = disk_log:open([{file, Kurt2}, {name, kurt2}, {type, wrap}, {size, {40, 4}}]), - ?line ok = disk_log:log(kurt2, "this is a logged message number X"), - ?line ok = disk_log:log(kurt2, "this is a logged message number Y"), - ?line {ok, BinK2} = file:read_file(add_ext(Kurt2, "idx")), - ?line <<0,2:32,1:32,1:32,1:32,1:32>> = BinK2, - ?line {{40,4}, 1} = disk_log_1:read_size_file_version(Kurt2), + ok = disk_log:log(kurt2, "this is a logged message number X"), + ok = disk_log:log(kurt2, "this is a logged message number Y"), + {ok, BinK2} = file:read_file(add_ext(Kurt2, "idx")), + <<0,2:32,1:32,1:32,1:32,1:32>> = BinK2, + {{40,4}, 1} = disk_log_1:read_size_file_version(Kurt2), disk_log:close(kurt2), - ?line del(Kurt2, 4), + del(Kurt2, 4), ok. -reopen(suite) -> []; -reopen(doc) -> - ["Test reopen/1 on halt and wrap logs."]; +%% Test reopen/1 on halt and wrap logs. reopen(Conf) when is_list(Conf) -> Dir = ?privdir(Conf), - ?line File = filename:join(Dir, "n.LOG"), - ?line NewFile = filename:join(Dir, "nn.LOG"), - ?line B = mk_bytes(60), - - ?line file:delete(File), % cleanup - ?line file:delete(NewFile), % cleanup - ?line Q = qlen(), + File = filename:join(Dir, "n.LOG"), + NewFile = filename:join(Dir, "nn.LOG"), + B = mk_bytes(60), + + file:delete(File), % cleanup + file:delete(NewFile), % cleanup + Q = qlen(), %% External halt log. - ?line {ok, n} = disk_log:open([{name, n}, {file, File}, {type, halt}, - {notify, true}, {head, "header"}, - {size, infinity},{format, external}]), - ?line ok = disk_log:blog(n, B), - ?line ok = disk_log:breopen(n, NewFile, "head"), - ?line rec(1, {disk_log, node(), n, {truncated, 2}}), - ?line ok = disk_log:blog(n, B), - ?line ok = disk_log:blog(n, B), - ?line ok = disk_log:breopen(n, NewFile, "head"), - ?line rec(1, {disk_log, node(), n, {truncated, 3}}), - ?line ok = disk_log:close(n), - ?line {ok,BinaryFile} = file:read_file(File), - ?line "head" = binary_to_list(BinaryFile), - ?line file:delete(File), - ?line file:delete(NewFile), + {ok, n} = disk_log:open([{name, n}, {file, File}, {type, halt}, + {notify, true}, {head, "header"}, + {size, infinity},{format, external}]), + ok = disk_log:blog(n, B), + ok = disk_log:breopen(n, NewFile, "head"), + rec(1, {disk_log, node(), n, {truncated, 2}}), + ok = disk_log:blog(n, B), + ok = disk_log:blog(n, B), + ok = disk_log:breopen(n, NewFile, "head"), + rec(1, {disk_log, node(), n, {truncated, 3}}), + ok = disk_log:close(n), + {ok,BinaryFile} = file:read_file(File), + "head" = binary_to_list(BinaryFile), + file:delete(File), + file:delete(NewFile), %% Internal halt log. - ?line {ok, n} = disk_log:open([{name, n}, {file, File}, {type, halt}, - {notify, true}, {head, header}, - {size, infinity}]), - ?line ok = disk_log:log(n, B), - ?line Error1 = {error, {same_file_name, n}} = disk_log:reopen(n, File), - ?line "Current and new" ++ _ = format_error(Error1), - ?line ok = disk_log:reopen(n, NewFile), - ?line rec(1, {disk_log, node(), n, {truncated, 2}}), - ?line ok = disk_log:log(n, B), - ?line ok = disk_log:log(n, B), - ?line ok = disk_log:reopen(n, NewFile), - ?line rec(1, {disk_log, node(), n, {truncated, 3}}), - ?line ok = disk_log:close(n), - ?line [header, _B, _B] = get_all_terms(nn, NewFile, halt), - ?line file:delete(File), - ?line file:delete(NewFile), + {ok, n} = disk_log:open([{name, n}, {file, File}, {type, halt}, + {notify, true}, {head, header}, + {size, infinity}]), + ok = disk_log:log(n, B), + Error1 = {error, {same_file_name, n}} = disk_log:reopen(n, File), + "Current and new" ++ _ = format_error(Error1), + ok = disk_log:reopen(n, NewFile), + rec(1, {disk_log, node(), n, {truncated, 2}}), + ok = disk_log:log(n, B), + ok = disk_log:log(n, B), + ok = disk_log:reopen(n, NewFile), + rec(1, {disk_log, node(), n, {truncated, 3}}), + ok = disk_log:close(n), + [header, _B, _B] = get_all_terms(nn, NewFile, halt), + file:delete(File), + file:delete(NewFile), %% Internal wrap log. - ?line No = 4, - ?line del(File, No), % cleanup - ?line del(NewFile, No), % cleanup - - ?line {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, - {notify, true}, - {head, header}, {size, {100, No}}]), - ?line ok = disk_log:log(n, B), - ?line ok = disk_log:log_terms(n, [B,B,B]), + No = 4, + del(File, No), % cleanup + del(NewFile, No), % cleanup + + {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, + {notify, true}, + {head, header}, {size, {100, No}}]), + ok = disk_log:log(n, B), + ok = disk_log:log_terms(n, [B,B,B]), %% Used to be one message, but now one per wrapped file. - ?line rec(3, {disk_log, node(), n, {wrap, 0}}), - ?line ok = disk_log:log_terms(n, [B]), - ?line rec(1, {disk_log, node(), n, {wrap, 2}}), - ?line ok = disk_log:log_terms(n, [B]), - ?line rec(1, {disk_log, node(), n, {wrap, 2}}), - ?line ok = disk_log:reopen(n, NewFile, new_header), - ?line rec(1, {disk_log, node(), n, {truncated, 8}}), - ?line ok = disk_log:log_terms(n, [B,B]), - ?line rec(1, {disk_log, node(), n, {wrap, 0}}), - ?line ok = disk_log:log_terms(n, [B]), - ?line rec(1, {disk_log, node(), n, {wrap, 0}}), - ?line ok = disk_log:close(n), - ?line [header, _, header, _, header, _, header, _] = + rec(3, {disk_log, node(), n, {wrap, 0}}), + ok = disk_log:log_terms(n, [B]), + rec(1, {disk_log, node(), n, {wrap, 2}}), + ok = disk_log:log_terms(n, [B]), + rec(1, {disk_log, node(), n, {wrap, 2}}), + ok = disk_log:reopen(n, NewFile, new_header), + rec(1, {disk_log, node(), n, {truncated, 8}}), + ok = disk_log:log_terms(n, [B,B]), + rec(1, {disk_log, node(), n, {wrap, 0}}), + ok = disk_log:log_terms(n, [B]), + rec(1, {disk_log, node(), n, {wrap, 0}}), + ok = disk_log:close(n), + [header, _, header, _, header, _, header, _] = get_all_terms(nn, NewFile, wrap), - ?line [new_header, _, header, _, header, _] = get_all_terms(n, File, wrap), + [new_header, _, header, _, header, _] = get_all_terms(n, File, wrap), - ?line del(NewFile, No), - ?line file:delete(File ++ ".2"), - ?line {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, - {notify, true}, - {head, header}, {size, {100, No}}]), + del(NewFile, No), + file:delete(File ++ ".2"), + {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, + {notify, true}, + {head, header}, {size, {100, No}}]), %% One file is missing... - ?line ok = disk_log:reopen(n, NewFile), - ?line rec(1, {disk_log, node(), n, {truncated, 6}}), - ?line ok = disk_log:close(n), + ok = disk_log:reopen(n, NewFile), + rec(1, {disk_log, node(), n, {truncated, 6}}), + ok = disk_log:close(n), - ?line del(File, No), - ?line del(NewFile, No), - ?line Q = qlen(), + del(File, No), + del(NewFile, No), + Q = qlen(), ok. -block_blocked(suite) -> []; -block_blocked(doc) -> - ["Test block/1 on external and internal logs."]; +%% Test block/1 on external and internal logs. block_blocked(Conf) when is_list(Conf) -> Dir = ?privdir(Conf), - ?line B = mk_bytes(60), + B = mk_bytes(60), Halt = join(Dir, "halt.LOG"), - % External logs. - ?line file:delete(Halt), % cleanup - ?line {ok, halt} = disk_log:open([{name, halt}, {type, halt}, - {format, external}, {file, Halt}]), - ?line ok = disk_log:sync(halt), - ?line ok = disk_log:block(halt, false), - ?line Error1 = {error, {blocked_log, halt}} = disk_log:block(halt), - ?line "The blocked disk" ++ _ = format_error(Error1), - ?line {error, {blocked_log, halt}} = disk_log:sync(halt), - ?line {error, {blocked_log, halt}} = disk_log:truncate(halt), - ?line {error, {blocked_log, halt}} = disk_log:change_size(halt, infinity), - ?line {error, {blocked_log, halt}} = + %% External logs. + file:delete(Halt), % cleanup + {ok, halt} = disk_log:open([{name, halt}, {type, halt}, + {format, external}, {file, Halt}]), + ok = disk_log:sync(halt), + ok = disk_log:block(halt, false), + Error1 = {error, {blocked_log, halt}} = disk_log:block(halt), + "The blocked disk" ++ _ = format_error(Error1), + {error, {blocked_log, halt}} = disk_log:sync(halt), + {error, {blocked_log, halt}} = disk_log:truncate(halt), + {error, {blocked_log, halt}} = disk_log:change_size(halt, infinity), + {error, {blocked_log, halt}} = disk_log:change_notify(halt, self(), false), - ?line {error, {blocked_log, halt}} = + {error, {blocked_log, halt}} = disk_log:change_header(halt, {head, header}), - ?line {error, {blocked_log, halt}} = disk_log:reopen(halt, "foo"), - ?line ok = disk_log:close(halt), - - ?line {ok, halt} = disk_log:open([{name, halt}, {type, halt}, - {format, external}]), - ?line ok = disk_log:sync(halt), - ?line ok = disk_log:block(halt, true), - ?line {error, {blocked_log, halt}} = disk_log:blog(halt, B), - ?line {error, {blocked_log, halt}} = disk_log:blog(halt, B), - ?line {error, {blocked_log, halt}} = disk_log:block(halt), - ?line {error, {blocked_log, halt}} = disk_log:sync(halt), - ?line {error, {blocked_log, halt}} = disk_log:truncate(halt), - ?line {error, {blocked_log, halt}} = disk_log:change_size(halt, infinity), - ?line {error, {blocked_log, halt}} = + {error, {blocked_log, halt}} = disk_log:reopen(halt, "foo"), + ok = disk_log:close(halt), + + {ok, halt} = disk_log:open([{name, halt}, {type, halt}, + {format, external}]), + ok = disk_log:sync(halt), + ok = disk_log:block(halt, true), + {error, {blocked_log, halt}} = disk_log:blog(halt, B), + {error, {blocked_log, halt}} = disk_log:blog(halt, B), + {error, {blocked_log, halt}} = disk_log:block(halt), + {error, {blocked_log, halt}} = disk_log:sync(halt), + {error, {blocked_log, halt}} = disk_log:truncate(halt), + {error, {blocked_log, halt}} = disk_log:change_size(halt, infinity), + {error, {blocked_log, halt}} = disk_log:change_notify(halt, self(), false), - ?line {error, {blocked_log, halt}} = + {error, {blocked_log, halt}} = disk_log:change_header(halt, {head, header}), - ?line {error, {blocked_log, halt}} = disk_log:reopen(halt, "foo"), - - ?line ok = disk_log:unblock(halt), - ?line ok = disk_log:close(halt), - ?line file:delete(Halt), - - % Internal logs. - ?line File = filename:join(Dir, "n.LOG"), - ?line No = 4, - ?line del(File, No), % cleanup - ?line {ok, halt} = disk_log:open([{name, halt}, {file, File}, {type, wrap}, - {size, {100, No}}]), - ?line ok = disk_log:block(halt, true), - ?line eof = disk_log:chunk(halt, start), - ?line Error2 = {error, end_of_log} = disk_log:chunk_step(halt, start, 1), - ?line "An attempt" ++ _ = format_error(Error2), - ?line {error, {blocked_log, halt}} = disk_log:log(halt, B), - ?line {error, {blocked_log, halt}} = disk_log:inc_wrap_file(halt), - ?line ok = disk_log:unblock(halt), - ?line ok = disk_log:block(halt, false), - ?line {error, {blocked_log, halt}} = disk_log:log(halt, B), - ?line {error, {blocked_log, halt}} = disk_log:inc_wrap_file(halt), - ?line Parent = self(), - ?line Pid = + {error, {blocked_log, halt}} = disk_log:reopen(halt, "foo"), + + ok = disk_log:unblock(halt), + ok = disk_log:close(halt), + file:delete(Halt), + + %% Internal logs. + File = filename:join(Dir, "n.LOG"), + No = 4, + del(File, No), % cleanup + {ok, halt} = disk_log:open([{name, halt}, {file, File}, {type, wrap}, + {size, {100, No}}]), + ok = disk_log:block(halt, true), + eof = disk_log:chunk(halt, start), + Error2 = {error, end_of_log} = disk_log:chunk_step(halt, start, 1), + "An attempt" ++ _ = format_error(Error2), + {error, {blocked_log, halt}} = disk_log:log(halt, B), + {error, {blocked_log, halt}} = disk_log:inc_wrap_file(halt), + ok = disk_log:unblock(halt), + ok = disk_log:block(halt, false), + {error, {blocked_log, halt}} = disk_log:log(halt, B), + {error, {blocked_log, halt}} = disk_log:inc_wrap_file(halt), + Parent = self(), + Pid = spawn_link(fun() -> {error, {blocked_log, halt}} = disk_log:chunk(halt, start), @@ -1660,109 +1606,107 @@ block_blocked(Conf) when is_list(Conf) -> disk_log:chunk_step(halt, start, 1), Parent ! {self(), stopped} end), - ?line receive {Pid,stopped} -> ok end, - ?line ok = disk_log:close(halt), - ?line del(File, No). + receive {Pid,stopped} -> ok end, + ok = disk_log:close(halt), + del(File, No). -block_queue(suite) -> []; -block_queue(doc) -> - ["Run commands from the queue by unblocking."]; +%% Run commands from the queue by unblocking. block_queue(Conf) when is_list(Conf) -> Dir = ?privdir(Conf), - ?line Q = qlen(), - ?line File = filename:join(Dir, "n.LOG"), - ?line No = 4, - ?line del(File, No), % cleanup - ?line B = mk_bytes(60), - - ?line Pid = spawn_link(?MODULE, lserv, [n]), - ?line {ok, n} = sync_do(Pid, {open, File}), - - ?line ok = disk_log:block(n, true), - ?line async_do(Pid, {blog, B}), - ?line ok = disk_log:unblock(n), - ?line ok = get_reply(), - ?line 1 = no_written_items(n), - ?line Error1 = {error,{not_blocked,n}} = disk_log:unblock(n), - ?line "The disk log" ++ _ = format_error(Error1), - - ?line ok = disk_log:block(n, true), - ?line async_do(Pid, {balog, "one string"}), - ?line ok = disk_log:unblock(n), - ?line ok = get_reply(), - ?line 2 = no_written_items(n), - - ?line ok = disk_log:block(n, true), - ?line async_do(Pid, sync), - ?line ok = disk_log:unblock(n), - ?line ok = get_reply(), - - ?line ok = disk_log:block(n, true), - ?line async_do(Pid, truncate), - ?line ok = disk_log:unblock(n), - ?line ok = get_reply(), - ?line 0 = no_items(n), - - ?line ok = disk_log:block(n, true), - ?line async_do(Pid, {block, false}), - ?line ok = disk_log:unblock(n), - ?line ok = get_reply(), - ?line {error, {blocked_log, _}} = disk_log:blog(n, B), - ?line ok = sync_do(Pid, unblock), - - ?line ok = disk_log:block(n, true), - ?line async_do(Pid, {change_notify, Pid, true}), - ?line ok = disk_log:unblock(n), - ?line ok = get_reply(), - ?line [{_, true}] = owners(n), - - ?line ok = disk_log:block(n, true), - ?line async_do(Pid, {change_notify, Pid, false}), - ?line ok = disk_log:unblock(n), - ?line ok = get_reply(), - ?line [{_, false}] = owners(n), - - ?line ok = disk_log:block(n, true), - ?line async_do(Pid, {change_header, {head, header}}), - ?line ok = disk_log:unblock(n), - ?line {error, {badarg, head}} = get_reply(), - - ?line ok = disk_log:block(n, true), - ?line async_do(Pid, {change_size, 17}), - ?line ok = disk_log:unblock(n), - ?line {error, {badarg, size}} = get_reply(), - - ?line ok = disk_log:block(n, true), - ?line async_do(Pid, inc_wrap_file), - ?line ok = disk_log:unblock(n), - ?line ok = get_reply(), - - ?line ok = sync_do(Pid, close), - ?line del(File, No), - - ?line _Pid2 = spawn_link(?MODULE, lserv, [n]), - ?line {ok, n} = sync_do(Pid, {int_open, File}), - - ?line ok = disk_log:block(n, true), - ?line async_do(Pid, {chunk, start}), - ?line ok = disk_log:unblock(n), - ?line eof = get_reply(), - - ?line ok = disk_log:block(n, true), - ?line async_do(Pid, {chunk_step, start, 100}), - ?line ok = disk_log:unblock(n), - ?line {ok, _Cont} = get_reply(), - - ?line ok = disk_log:block(n, true), - ?line async_do(Pid, {log,a_term}), - ?line ok = disk_log:unblock(n), - ?line ok = get_reply(), - ?line 1 = no_written_items(n), - - ?line ok = sync_do(Pid, close), - ?line sync_do(Pid, terminate), - ?line del(File, No), + Q = qlen(), + File = filename:join(Dir, "n.LOG"), + No = 4, + del(File, No), % cleanup + B = mk_bytes(60), + + Pid = spawn_link(?MODULE, lserv, [n]), + {ok, n} = sync_do(Pid, {open, File}), + + ok = disk_log:block(n, true), + async_do(Pid, {blog, B}), + ok = disk_log:unblock(n), + ok = get_reply(), + 1 = no_written_items(n), + Error1 = {error,{not_blocked,n}} = disk_log:unblock(n), + "The disk log" ++ _ = format_error(Error1), + + ok = disk_log:block(n, true), + async_do(Pid, {balog, "one string"}), + ok = disk_log:unblock(n), + ok = get_reply(), + 2 = no_written_items(n), + + ok = disk_log:block(n, true), + async_do(Pid, sync), + ok = disk_log:unblock(n), + ok = get_reply(), + + ok = disk_log:block(n, true), + async_do(Pid, truncate), + ok = disk_log:unblock(n), + ok = get_reply(), + 0 = no_items(n), + + ok = disk_log:block(n, true), + async_do(Pid, {block, false}), + ok = disk_log:unblock(n), + ok = get_reply(), + {error, {blocked_log, _}} = disk_log:blog(n, B), + ok = sync_do(Pid, unblock), + + ok = disk_log:block(n, true), + async_do(Pid, {change_notify, Pid, true}), + ok = disk_log:unblock(n), + ok = get_reply(), + [{_, true}] = owners(n), + + ok = disk_log:block(n, true), + async_do(Pid, {change_notify, Pid, false}), + ok = disk_log:unblock(n), + ok = get_reply(), + [{_, false}] = owners(n), + + ok = disk_log:block(n, true), + async_do(Pid, {change_header, {head, header}}), + ok = disk_log:unblock(n), + {error, {badarg, head}} = get_reply(), + + ok = disk_log:block(n, true), + async_do(Pid, {change_size, 17}), + ok = disk_log:unblock(n), + {error, {badarg, size}} = get_reply(), + + ok = disk_log:block(n, true), + async_do(Pid, inc_wrap_file), + ok = disk_log:unblock(n), + ok = get_reply(), + + ok = sync_do(Pid, close), + del(File, No), + + _Pid2 = spawn_link(?MODULE, lserv, [n]), + {ok, n} = sync_do(Pid, {int_open, File}), + + ok = disk_log:block(n, true), + async_do(Pid, {chunk, start}), + ok = disk_log:unblock(n), + eof = get_reply(), + + ok = disk_log:block(n, true), + async_do(Pid, {chunk_step, start, 100}), + ok = disk_log:unblock(n), + {ok, _Cont} = get_reply(), + + ok = disk_log:block(n, true), + async_do(Pid, {log,a_term}), + ok = disk_log:unblock(n), + ok = get_reply(), + 1 = no_written_items(n), + + ok = sync_do(Pid, close), + sync_do(Pid, terminate), + del(File, No), %% Test of the queue. Three processes involved here. Pid1's block %% request is queued. Pid2's log requests are put in the queue. @@ -1770,171 +1714,165 @@ block_queue(Conf) when is_list(Conf) -> %% Pid2's log requests are executed when Pid1 unblocks. %% (This example should show that the pair 'queue' and 'messages' %% in State does the trick - one does not need a "real" queue.) - ?line P0 = pps(), + P0 = pps(), Name = n, - ?line Pid1 = spawn_link(?MODULE, lserv, [Name]), - ?line {ok, Name} = sync_do(Pid1, {int_open, File, {1000,2}}), - ?line Pid2 = spawn_link(?MODULE, lserv, [Name]), - ?line {ok, Name} = sync_do(Pid2, {int_open, File, {1000,2}}), - ?line ok = disk_log:block(Name), - ?line async_do(Pid1, {alog,{1,a}}), - ?line ok = get_reply(), - ?line async_do(Pid1, {alog,{2,b}}), - ?line ok = get_reply(), - ?line async_do(Pid1, {alog,{3,c}}), - ?line ok = get_reply(), - ?line async_do(Pid1, {alog,{4,d}}), - ?line ok = get_reply(), - ?line async_do(Pid1, block), - ?line async_do(Pid2, {alog,{5,e}}), - ?line ok = get_reply(), - ?line async_do(Pid2, {alog,{6,f}}), - ?line ok = get_reply(), - ?line ok = disk_log:unblock(Name), - ?line ok = get_reply(), - ?line async_do(Pid2, {alog,{7,g}}), - ?line ok = get_reply(), - ?line async_do(Pid2, {alog,{8,h}}), - ?line ok = get_reply(), - ?line async_do(Pid1, unblock), - ?line ok = get_reply(), - ?line ok = sync_do(Pid1, close), - ?line ok = sync_do(Pid2, close), - ?line sync_do(Pid1, terminate), - ?line sync_do(Pid2, terminate), + Pid1 = spawn_link(?MODULE, lserv, [Name]), + {ok, Name} = sync_do(Pid1, {int_open, File, {1000,2}}), + Pid2 = spawn_link(?MODULE, lserv, [Name]), + {ok, Name} = sync_do(Pid2, {int_open, File, {1000,2}}), + ok = disk_log:block(Name), + async_do(Pid1, {alog,{1,a}}), + ok = get_reply(), + async_do(Pid1, {alog,{2,b}}), + ok = get_reply(), + async_do(Pid1, {alog,{3,c}}), + ok = get_reply(), + async_do(Pid1, {alog,{4,d}}), + ok = get_reply(), + async_do(Pid1, block), + async_do(Pid2, {alog,{5,e}}), + ok = get_reply(), + async_do(Pid2, {alog,{6,f}}), + ok = get_reply(), + ok = disk_log:unblock(Name), + ok = get_reply(), + async_do(Pid2, {alog,{7,g}}), + ok = get_reply(), + async_do(Pid2, {alog,{8,h}}), + ok = get_reply(), + async_do(Pid1, unblock), + ok = get_reply(), + ok = sync_do(Pid1, close), + ok = sync_do(Pid2, close), + sync_do(Pid1, terminate), + sync_do(Pid2, terminate), Terms = get_all_terms(Name, File, wrap), - ?line true = [{1,a},{2,b},{3,c},{4,d},{5,e},{6,f},{7,g},{8,h}] == Terms, + true = [{1,a},{2,b},{3,c},{4,d},{5,e},{6,f},{7,g},{8,h}] == Terms, del(File, 2), - ?line Q = qlen(), - ?line true = (P0 == pps()), + Q = qlen(), + check_pps(P0), ok. -block_queue2(suite) -> []; -block_queue2(doc) -> - ["OTP-4880. Blocked processes did not get disk_log_stopped message."]; +%% OTP-4880. Blocked processes did not get disk_log_stopped message. block_queue2(Conf) when is_list(Conf) -> - ?line Q = qlen(), - ?line P0 = pps(), + Q = qlen(), + P0 = pps(), Dir = ?privdir(Conf), - ?line File = filename:join(Dir, "n.LOG"), - ?line No = 4, + File = filename:join(Dir, "n.LOG"), + No = 4, %% log requests are queued, and processed when the log is closed - ?line Pid = spawn_link(?MODULE, lserv, [n]), - ?line {ok, n} = sync_do(Pid, {open, File}), - ?line ok = sync_do(Pid, block), + Pid = spawn_link(?MODULE, lserv, [n]), + {ok, n} = sync_do(Pid, {open, File}), + ok = sync_do(Pid, block), %% Asynchronous stuff is ignored. - ?line ok = disk_log:balog_terms(n, [<<"foo">>,<<"bar">>]), - ?line ok = disk_log:balog_terms(n, [<<"more">>,<<"terms">>]), + ok = disk_log:balog_terms(n, [<<"foo">>,<<"bar">>]), + ok = disk_log:balog_terms(n, [<<"more">>,<<"terms">>]), Parent = self(), - ?line Fun = + Fun = fun() -> {error,no_such_log} = disk_log:sync(n), receive {disk_log, _, {error, disk_log_stopped}} -> ok end, Parent ! disk_log_stopped_ok end, - ?line spawn(Fun), - ?line ok = sync_do(Pid, close), - ?line receive disk_log_stopped_ok -> ok end, - ?line sync_do(Pid, terminate), - ?line {ok,<<>>} = file:read_file(File ++ ".1"), - ?line del(File, No), - ?line Q = qlen(), - ?line true = (P0 == pps()), + spawn(Fun), + ok = sync_do(Pid, close), + receive disk_log_stopped_ok -> ok end, + sync_do(Pid, terminate), + {ok,<<>>} = file:read_file(File ++ ".1"), + del(File, No), + Q = qlen(), + check_pps(P0), ok. -unblock(suite) -> []; -unblock(doc) -> - ["Test unblock/1."]; +%% Test unblock/1. unblock(Conf) when is_list(Conf) -> Dir = ?privdir(Conf), File = filename:join(Dir, "n.LOG"), No = 1, - ?line {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, - {size, {100,No}}, {notify, true}, - {format, external}]), - ?line ok = disk_log:block(n), - ?line spawn_link(?MODULE, try_unblock, [n]), - ?line timer:sleep(100), - ?line disk_log:close(n), - ?line del(File, No). + {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, + {size, {100,No}}, {notify, true}, + {format, external}]), + ok = disk_log:block(n), + spawn_link(?MODULE, try_unblock, [n]), + timer:sleep(100), + disk_log:close(n), + del(File, No). try_unblock(Log) -> - ?line Error = {error, {not_blocked_by_pid, n}} = disk_log:unblock(Log), - ?line "The disk log" ++ _ = format_error(Error). + Error = {error, {not_blocked_by_pid, n}} = disk_log:unblock(Log), + "The disk log" ++ _ = format_error(Error). -open_overwrite(suite) -> []; -open_overwrite(doc) -> - ["Test open/1 when old files exist."]; +%% Test open/1 when old files exist. open_overwrite(Conf) when is_list(Conf) -> Dir = ?privdir(Conf), - ?line File = filename:join(Dir, "n.LOG"), - ?line No = 4, - ?line del(File, No), % cleanup + File = filename:join(Dir, "n.LOG"), + No = 4, + del(File, No), % cleanup - % read write - ?line First = "n.LOG.1", - ?line make_file(Dir, First, 8), + %% read write + First = "n.LOG.1", + make_file(Dir, First, 8), - ?line Error1 = {error, {not_a_log_file, _}} = + Error1 = {error, {not_a_log_file, _}} = disk_log:open([{name, n}, {file, File}, {type, wrap}, {format, internal}, {size, {100, No}}]), - ?line "The file" ++ _ = format_error(Error1), - ?line del(File, No), + "The file" ++ _ = format_error(Error1), + del(File, No), - ?line make_file(Dir, First, 4), + make_file(Dir, First, 4), - ?line {error, {not_a_log_file, _}} = + {error, {not_a_log_file, _}} = disk_log:open([{name, n}, {file, File}, {type, wrap}, {format, internal}, {size, {100, No}}]), - ?line del(File, No), + del(File, No), - ?line make_file(Dir, First, 0), + make_file(Dir, First, 0), - ?line {error, {not_a_log_file, _}} = + {error, {not_a_log_file, _}} = disk_log:open([{name, n}, {file, File}, {type, wrap}, {format, internal}, {size, {100, No}}]), - % read only - ?line make_file(Dir, First, 6), + %% read only + make_file(Dir, First, 6), - ?line {error, {not_a_log_file, _}} = + {error, {not_a_log_file, _}} = disk_log:open([{name, n}, {file, File}, {type, wrap},{mode, read_only}, {format, internal}, {size, {100, No}}]), - ?line del(File, No), + del(File, No), - ?line make_file(Dir, First, 0), + make_file(Dir, First, 0), - ?line {error, {not_a_log_file, _}} = + {error, {not_a_log_file, _}} = disk_log:open([{name, n}, {file, File},{type, wrap}, {mode, read_only}, {format, internal}, {size, {100, No}}]), - ?line del(File, No), - - ?line {error, _} = disk_log:open([{name, n}, {file, File}, {type, wrap}, - {mode, read_only}, - {format, internal},{size, {100, No}}]), + del(File, No), + + {error, _} = disk_log:open([{name, n}, {file, File}, {type, wrap}, + {mode, read_only}, + {format, internal},{size, {100, No}}]), file:delete(File), - ?line {ok,n} = disk_log:open([{name,n},{file,File}, - {mode,read_write},{type,halt}]), - ?line ok = disk_log:close(n), - ?line ok = unwritable(File), - ?line {error, {file_error, File, _}} = - disk_log:open([{name,n},{file,File},{mode,read_write},{type,halt}]), - ?line ok = writable(File), + {ok,n} = disk_log:open([{name,n},{file,File}, + {mode,read_write},{type,halt}]), + ok = disk_log:close(n), + ok = unwritable(File), + {error, {file_error, File, _}} = + disk_log:open([{name,n},{file,File},{mode,read_write},{type,halt}]), + ok = writable(File), file:delete(File), - ?line {ok,n} = disk_log:open([{name,n},{file,File},{format,external}, - {mode,read_write},{type,halt}]), - ?line ok = disk_log:close(n), - ?line ok = unwritable(File), - ?line {error, {file_error, File, _}} = + {ok,n} = disk_log:open([{name,n},{file,File},{format,external}, + {mode,read_write},{type,halt}]), + ok = disk_log:close(n), + ok = unwritable(File), + {error, {file_error, File, _}} = disk_log:open([{name,n},{file,File},{format,external}, {mode,read_write},{type,halt}]), - ?line ok = writable(File), + ok = writable(File), file:delete(File), ok. @@ -1952,424 +1890,412 @@ make_file(Dir, File, N) -> end, ok = file:close(F). -open_size(suite) -> []; -open_size(doc) -> - ["Test open/1 option size."]; +%% Test open/1 option size. open_size(Conf) when is_list(Conf) -> - ?line Dir = ?privdir(Conf), - ?line File = filename:join(Dir, "n.LOG"), + Dir = ?privdir(Conf), + File = filename:join(Dir, "n.LOG"), - ?line No = 4, - ?line file:delete(File), - ?line del(File, No), % cleanup + No = 4, + file:delete(File), + del(File, No), % cleanup %% missing size option - ?line {error, {badarg, size}} = + {error, {badarg, size}} = disk_log:open([{name, n}, {file, File}, {type, wrap}, {format, internal}]), - ?line {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, + {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, {format, internal},{size, {100, No}}]), - ?line B = mk_bytes(60), - ?line ok = disk_log:log_terms(n, [B, B, B, B]), - ?line ok = disk_log:sync(n), - ?line ok = disk_log:block(n), + B = mk_bytes(60), + ok = disk_log:log_terms(n, [B, B, B, B]), + ok = disk_log:sync(n), + ok = disk_log:block(n), %% size option does not match existing size file, read_only - ?line Error1 = {error, {size_mismatch, _, _}} = + Error1 = {error, {size_mismatch, _, _}} = disk_log:open([{name, nn}, {file, File}, {type, wrap}, {mode, read_only}, {format, internal}, {size, {100, No + 1}}]), - ?line "The given size" ++ _ = format_error(Error1), - ?line {ok, nn} = disk_log:open([{name, nn}, {file, File}, {type, wrap}, + "The given size" ++ _ = format_error(Error1), + {ok, nn} = disk_log:open([{name, nn}, {file, File}, {type, wrap}, {mode, read_only}, {format, internal},{size, {100, No}}]), - ?line [_, _, _, _] = get_all_terms1(nn, start, []), - ?line disk_log:close(nn), + [_, _, _, _] = get_all_terms1(nn, start, []), + disk_log:close(nn), - ?line ok = disk_log:unblock(n), - ?line ok = disk_log:close(n), + ok = disk_log:unblock(n), + ok = disk_log:close(n), %% size option does not match existing size file, read_write - ?line {error, {size_mismatch, _, _}} = + {error, {size_mismatch, _, _}} = disk_log:open([{name, nn}, {file, File}, {type, wrap}, {format, internal}, {size, {100, No + 1}}]), %% size option does not match existing size file, truncating - ?line {ok, nn} = + {ok, nn} = disk_log:open([{name, nn}, {file, File}, {type, wrap}, {repair, truncate}, {format, internal}, {size, {100, No + 1}}]), - ?line ok = disk_log:close(nn), + ok = disk_log:close(nn), - ?line del(File, No), + del(File, No), ok. -open_truncate(suite) -> []; -open_truncate(doc) -> - ["Test open/1 with {repair, truncate}."]; +%% Test open/1 with {repair, truncate}. open_truncate(Conf) when is_list(Conf) -> - ?line Dir = ?privdir(Conf), - ?line File = filename:join(Dir, "n.LOG"), - ?line No = 4, - ?line del(File, No), % cleanup - - ?line {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, - {format, internal},{size, {100, No}}]), - ?line B = mk_bytes(60), - ?line ok = disk_log:log_terms(n, [B, B, B, B]), - ?line ok = disk_log:close(n), - ?line {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, - {repair,truncate}, - {format, internal},{size, {100, No}}]), - ?line ok = disk_log:close(n), - ?line [] = get_all_terms(n, File, wrap), - ?line del(File, No), + Dir = ?privdir(Conf), + File = filename:join(Dir, "n.LOG"), + No = 4, + del(File, No), % cleanup + + {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, + {format, internal},{size, {100, No}}]), + B = mk_bytes(60), + ok = disk_log:log_terms(n, [B, B, B, B]), + ok = disk_log:close(n), + {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, + {repair,truncate}, + {format, internal},{size, {100, No}}]), + ok = disk_log:close(n), + [] = get_all_terms(n, File, wrap), + del(File, No), ok. - -open_error(suite) -> []; -open_error(doc) -> - ["Try some invalid open/1 options."]; + +%% Try some invalid open/1 options. open_error(Conf) when is_list(Conf) -> - ?line Dir = ?privdir(Conf), + Dir = ?privdir(Conf), - ?line File = filename:join(Dir, "n.LOG"), - ?line No = 4, - ?line del(File, No), % cleanup + File = filename:join(Dir, "n.LOG"), + No = 4, + del(File, No), % cleanup - ?line {error, {badarg, name}} = disk_log:open([{file, File}]), - ?line {error, {badarg, file}} = disk_log:open([{name,{foo,bar}}]), - ?line {error, {badarg, [{foo,bar}]}} = disk_log:open([{foo,bar}]), + {error, {badarg, name}} = disk_log:open([{file, File}]), + {error, {badarg, file}} = disk_log:open([{name,{foo,bar}}]), + {error, {badarg, [{foo,bar}]}} = disk_log:open([{foo,bar}]), %% external logs, read_only. - ?line {error, {file_error, _, enoent}} = + {error, {file_error, _, enoent}} = disk_log:open([{name, n}, {file, File}, {type, wrap}, {size, {100,No}}, {format, external}, {mode, read_only}]), - ?line Error5 = {error, {file_error, _, enoent}} = + Error5 = {error, {file_error, _, enoent}} = disk_log:open([{name, n}, {file, File}, {type, halt}, {size, 100}, {format, external}, {mode, read_only}]), - ?line true = lists:prefix("\"" ++ File, format_error(Error5)), + true = lists:prefix("\"" ++ File, format_error(Error5)), - ?line {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, + {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, {format, external},{size, {100, No}}]), %% Already owner, ignored. - ?line {ok, n} = + {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, {format, external}, {size, {100, No}}]), - ?line Error2 = {error, {name_already_open, n}} = + Error2 = {error, {name_already_open, n}} = disk_log:open([{name, n}, {file, another_file}, {type, wrap}, {format, external}, {size, {100, No}}]), - ?line "The disk log" ++ _ = format_error(Error2), - ?line Error1 = {error, {arg_mismatch, notify, false, true}} = + "The disk log" ++ _ = format_error(Error2), + Error1 = {error, {arg_mismatch, notify, false, true}} = disk_log:open([{name, n}, {file, File}, {type, wrap}, {format, external}, {size, {100, No}}, {notify, true}]), - ?line "The value" ++ _ = format_error(Error1), - ?line Error3 = {error, {open_read_write, n}} = + "The value" ++ _ = format_error(Error1), + Error3 = {error, {open_read_write, n}} = disk_log:open([{name, n}, {file, File}, {type, wrap}, {mode, read_only}, {format, external}, {size, {100, No}}]), - ?line "The disk log" ++ _ = format_error(Error3), - ?line {error, {badarg, size}} = + "The disk log" ++ _ = format_error(Error3), + {error, {badarg, size}} = disk_log:open([{name, n}, {file, File}, {type, halt}, {format, external}, {size, {100, No}}]), - ?line {error, {arg_mismatch, type, wrap, halt}} = + {error, {arg_mismatch, type, wrap, halt}} = disk_log:open([{name, n}, {file, File}, {type, halt}, {format, external}]), - ?line {error, {arg_mismatch, format, external, internal}} = + {error, {arg_mismatch, format, external, internal}} = disk_log:open([{name, n}, {file, File}, {type, wrap}, {format, internal}, {size, {100, No}}]), - ?line {error, {arg_mismatch, repair, true, false}} = + {error, {arg_mismatch, repair, true, false}} = disk_log:open([{name, n}, {file, File}, {type, wrap}, {format, external}, {repair, false}]), - ?line {error, {size_mismatch, {100,4}, {1000,4}}} = + {error, {size_mismatch, {100,4}, {1000,4}}} = disk_log:open([{name, n}, {file, File}, {type, wrap}, {format, external}, {size, {1000, No}}]), - ?line {error, {arg_mismatch, head, none, _}} = + {error, {arg_mismatch, head, none, _}} = disk_log:open([{name, n}, {file, File}, {type, wrap}, {head, "header"}, {format, external}, {size, {100, No}}]), - ?line {error, {badarg, size}} = + {error, {badarg, size}} = disk_log:open([{name, n}, {file, File}, {type, wrap}, {format, external}, {size, 100}]), - ?line ok = disk_log:close(n), + ok = disk_log:close(n), - ?line {ok, n} = + {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, {mode, read_only}, {format, external}, {size, {100, No}}]), - ?line Error4 = {error, {open_read_only, n}} = + Error4 = {error, {open_read_only, n}} = disk_log:open([{name, n}, {file, File}, {type, wrap}, {mode, read_write}, {format, external}, {size, {100, No}}]), - ?line "The disk log" ++ _ = format_error(Error4), - ?line ok = disk_log:close(n), + "The disk log" ++ _ = format_error(Error4), + ok = disk_log:close(n), - ?line del(File, No). + del(File, No). -close_race(suite) -> []; -close_race(doc) -> - ["Do something quickly after close/1"]; +%% Do something quickly after close/1. close_race(Conf) when is_list(Conf) -> Dir = ?privdir(Conf), - ?line File = filename:join(Dir, "n.LOG"), - ?line No = 1, - ?line del(File, No), % cleanup - ?line {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, - {size, {100,No}}, {notify, true}, - {format, internal}]), - ?line ok = disk_log:close(n), - ?line Error1 = {error, no_such_log} = disk_log:close(n), - ?line "There is no disk" ++ _ = format_error(Error1), - - % Pid1 blocks, Pid2 closes without being suspended. - ?line Pid1 = spawn_link(?MODULE, lserv, [n]), - ?line Pid2 = spawn_link(?MODULE, lserv, [n]), - ?line {ok, n} = sync_do(Pid1, {open, File}), - ?line {ok, n} = sync_do(Pid2, {open, File}), - ?line ok = sync_do(Pid1, block), - ?line [{_, false}, {_, false}] = sync_do(Pid1, owners), - ?line ok = sync_do(Pid2, close), - ?line [{_, false}] = sync_do(Pid1, owners), - ?line ok = sync_do(Pid1, close), - ?line sync_do(Pid1, terminate), - ?line sync_do(Pid2, terminate), - ?line {error, no_such_log} = disk_log:info(n), - - % Pid3 blocks, Pid3 closes. Pid4 should still be ablo to use log. - ?line Pid3 = spawn_link(?MODULE, lserv, [n]), - ?line Pid4 = spawn_link(?MODULE, lserv, [n]), - ?line {ok, n} = sync_do(Pid3, {open, File}), - ?line {ok, n} = sync_do(Pid4, {open, File}), - ?line ok = sync_do(Pid3, block), - ?line ok = sync_do(Pid3, close), - ?line [{_Pid4, false}] = sync_do(Pid4, owners), - ?line sync_do(Pid3, terminate), - ?line sync_do(Pid4, terminate), - ?line {error, no_such_log} = disk_log:info(n), - - % Pid5 blocks, Pid5 terminates. Pid6 should still be ablo to use log. - ?line Pid5 = spawn_link(?MODULE, lserv, [n]), - ?line Pid6 = spawn_link(?MODULE, lserv, [n]), - ?line {ok, n} = sync_do(Pid5, {open, File}), - ?line {ok, n} = sync_do(Pid6, {open, File}), - ?line ok = sync_do(Pid5, block), - ?line sync_do(Pid5, terminate), - ?line [{_Pid6, false}] = sync_do(Pid6, owners), - ?line sync_do(Pid6, terminate), - ?line {error, no_such_log} = disk_log:info(n), - ?line del(File, No), % cleanup + File = filename:join(Dir, "n.LOG"), + No = 1, + del(File, No), % cleanup + {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, + {size, {100,No}}, {notify, true}, + {format, internal}]), + ok = disk_log:close(n), + Error1 = {error, no_such_log} = disk_log:close(n), + "There is no disk" ++ _ = format_error(Error1), + + %% Pid1 blocks, Pid2 closes without being suspended. + Pid1 = spawn_link(?MODULE, lserv, [n]), + Pid2 = spawn_link(?MODULE, lserv, [n]), + {ok, n} = sync_do(Pid1, {open, File}), + {ok, n} = sync_do(Pid2, {open, File}), + ok = sync_do(Pid1, block), + [{_, false}, {_, false}] = sync_do(Pid1, owners), + ok = sync_do(Pid2, close), + [{_, false}] = sync_do(Pid1, owners), + ok = sync_do(Pid1, close), + sync_do(Pid1, terminate), + sync_do(Pid2, terminate), + {error, no_such_log} = disk_log:info(n), + + %% Pid3 blocks, Pid3 closes. Pid4 should still be ablo to use log. + Pid3 = spawn_link(?MODULE, lserv, [n]), + Pid4 = spawn_link(?MODULE, lserv, [n]), + {ok, n} = sync_do(Pid3, {open, File}), + {ok, n} = sync_do(Pid4, {open, File}), + ok = sync_do(Pid3, block), + ok = sync_do(Pid3, close), + [{_Pid4, false}] = sync_do(Pid4, owners), + sync_do(Pid3, terminate), + sync_do(Pid4, terminate), + {error, no_such_log} = disk_log:info(n), + + %% Pid5 blocks, Pid5 terminates. Pid6 should still be ablo to use log. + Pid5 = spawn_link(?MODULE, lserv, [n]), + Pid6 = spawn_link(?MODULE, lserv, [n]), + {ok, n} = sync_do(Pid5, {open, File}), + {ok, n} = sync_do(Pid6, {open, File}), + ok = sync_do(Pid5, block), + sync_do(Pid5, terminate), + [{_Pid6, false}] = sync_do(Pid6, owners), + sync_do(Pid6, terminate), + {error, no_such_log} = disk_log:info(n), + del(File, No), % cleanup ok. -close_block(suite) -> []; -close_block(doc) -> - ["Block, unblock, close, terminate."]; +%% Block, unblock, close, terminate. close_block(Conf) when is_list(Conf) -> Dir = ?privdir(Conf), - ?line File = filename:join(Dir, "n.LOG"), + File = filename:join(Dir, "n.LOG"), No = 1, del(File, No), % cleanup P0 = pps(), %% One of two owners terminates. - ?line Pid1 = spawn_link(?MODULE, lserv, [n]), - ?line Pid2 = spawn_link(?MODULE, lserv, [n]), - ?line {ok, n} = sync_do(Pid1, {open, File}), - ?line {ok, n} = sync_do(Pid2, {open, File}), - ?line [_, _] = sync_do(Pid1, owners), - ?line [_, _] = sync_do(Pid2, owners), - ?line 0 = sync_do(Pid1, users), - ?line 0 = sync_do(Pid2, users), - ?line sync_do(Pid1, terminate), - ?line [_] = sync_do(Pid2, owners), - ?line 0 = sync_do(Pid2, users), - ?line sync_do(Pid2, terminate), - ?line {error, no_such_log} = disk_log:info(n), - ?line true = (P0 == pps()), + Pid1 = spawn_link(?MODULE, lserv, [n]), + Pid2 = spawn_link(?MODULE, lserv, [n]), + {ok, n} = sync_do(Pid1, {open, File}), + {ok, n} = sync_do(Pid2, {open, File}), + [_, _] = sync_do(Pid1, owners), + [_, _] = sync_do(Pid2, owners), + 0 = sync_do(Pid1, users), + 0 = sync_do(Pid2, users), + sync_do(Pid1, terminate), + [_] = sync_do(Pid2, owners), + 0 = sync_do(Pid2, users), + sync_do(Pid2, terminate), + {error, no_such_log} = disk_log:info(n), + check_pps(P0), %% Users terminate (no link...). - ?line Pid3 = spawn_link(?MODULE, lserv, [n]), - ?line Pid4 = spawn_link(?MODULE, lserv, [n]), - ?line {ok, n} = sync_do(Pid3, {open, File, none}), - ?line {ok, n} = sync_do(Pid4, {open, File, none}), - ?line [] = sync_do(Pid3, owners), - ?line [] = sync_do(Pid4, owners), - ?line 2 = sync_do(Pid3, users), - ?line 2 = sync_do(Pid4, users), - ?line sync_do(Pid3, terminate), - ?line [] = sync_do(Pid4, owners), - ?line 2 = sync_do(Pid4, users), - ?line sync_do(Pid4, terminate), - ?line disk_log:close(n), - ?line disk_log:close(n), - ?line {error, no_such_log} = disk_log:info(n), - ?line true = (P0 == pps()), - - % Blocking owner terminates. - ?line Pid5 = spawn_link(?MODULE, lserv, [n]), - ?line {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, - {linkto, none},{size, {100,No}}, - {format, external}]), - ?line {ok, n} = sync_do(Pid5, {open, File}), - ?line ok = sync_do(Pid5, block), - ?line {blocked, true} = status(n), - ?line [_] = owners(n), - ?line sync_do(Pid5, terminate), - ?line ok = status(n), - ?line [] = owners(n), - ?line 1 = users(n), - ?line ok = disk_log:close(n), - ?line {error, no_such_log} = disk_log:info(n), - ?line true = (P0 == pps()), - - % Blocking user terminates. - ?line Pid6 = spawn_link(?MODULE, lserv, [n]), - ?line {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, - {size, {100,No}}, {format, external}]), - ?line {ok, n} = sync_do(Pid6, {open, File, none}), - ?line ok = sync_do(Pid6, block), - ?line {blocked, true} = status(n), - ?line [_] = owners(n), - ?line 1 = users(n), - ?line sync_do(Pid6, terminate), % very silently... - ?line ok = status(n), - ?line [_] = owners(n), - ?line 1 = users(n), - ?line ok = disk_log:close(n), - ?line [] = owners(n), - ?line 1 = users(n), - ?line ok = disk_log:close(n), - ?line {error, no_such_log} = disk_log:info(n), - ?line true = (P0 == pps()), - - % Blocking owner terminates. - ?line Pid7 = spawn_link(?MODULE, lserv, [n]), - ?line {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, - {linkto, none}, - {size, {100,No}}, {format, external}]), - ?line {ok, n} = sync_do(Pid7, {open, File}), - ?line ok = sync_do(Pid7, block), - ?line {blocked, true} = status(n), - ?line [_] = owners(n), - ?line 1 = users(n), - ?line sync_do(Pid7, terminate), - ?line ok = status(n), - ?line [] = owners(n), - ?line 1 = users(n), - ?line ok = disk_log:close(n), - ?line {error, no_such_log} = disk_log:info(n), - ?line true = (P0 == pps()), + Pid3 = spawn_link(?MODULE, lserv, [n]), + Pid4 = spawn_link(?MODULE, lserv, [n]), + {ok, n} = sync_do(Pid3, {open, File, none}), + {ok, n} = sync_do(Pid4, {open, File, none}), + [] = sync_do(Pid3, owners), + [] = sync_do(Pid4, owners), + 2 = sync_do(Pid3, users), + 2 = sync_do(Pid4, users), + sync_do(Pid3, terminate), + [] = sync_do(Pid4, owners), + 2 = sync_do(Pid4, users), + sync_do(Pid4, terminate), + disk_log:close(n), + disk_log:close(n), + {error, no_such_log} = disk_log:info(n), + check_pps(P0), + + %% Blocking owner terminates. + Pid5 = spawn_link(?MODULE, lserv, [n]), + {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, + {linkto, none},{size, {100,No}}, + {format, external}]), + {ok, n} = sync_do(Pid5, {open, File}), + ok = sync_do(Pid5, block), + {blocked, true} = status(n), + [_] = owners(n), + sync_do(Pid5, terminate), + ok = status(n), + [] = owners(n), + 1 = users(n), + ok = disk_log:close(n), + {error, no_such_log} = disk_log:info(n), + check_pps(P0), + + %% Blocking user terminates. + Pid6 = spawn_link(?MODULE, lserv, [n]), + {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, + {size, {100,No}}, {format, external}]), + {ok, n} = sync_do(Pid6, {open, File, none}), + ok = sync_do(Pid6, block), + {blocked, true} = status(n), + [_] = owners(n), + 1 = users(n), + sync_do(Pid6, terminate), % very silently... + ok = status(n), + [_] = owners(n), + 1 = users(n), + ok = disk_log:close(n), + [] = owners(n), + 1 = users(n), + ok = disk_log:close(n), + {error, no_such_log} = disk_log:info(n), + check_pps(P0), + + %% Blocking owner terminates. + Pid7 = spawn_link(?MODULE, lserv, [n]), + {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, + {linkto, none}, + {size, {100,No}}, {format, external}]), + {ok, n} = sync_do(Pid7, {open, File}), + ok = sync_do(Pid7, block), + {blocked, true} = status(n), + [_] = owners(n), + 1 = users(n), + sync_do(Pid7, terminate), + ok = status(n), + [] = owners(n), + 1 = users(n), + ok = disk_log:close(n), + {error, no_such_log} = disk_log:info(n), + check_pps(P0), %% Two owners, the blocking one terminates. - ?line Pid8 = spawn_link(?MODULE, lserv, [n]), - ?line Pid9 = spawn_link(?MODULE, lserv, [n]), - ?line {ok, n} = sync_do(Pid8, {open, File}), - ?line {ok, n} = sync_do(Pid9, {open, File}), - ?line ok = sync_do(Pid8, block), - ?line {blocked, true} = status(n), - ?line sync_do(Pid8, terminate), - ?line ok = status(n), - ?line [_] = sync_do(Pid9, owners), - ?line 0 = sync_do(Pid9, users), - ?line sync_do(Pid9, terminate), - ?line {error, no_such_log} = disk_log:info(n), - ?line true = (P0 == pps()), - - % Blocking user closes. - ?line Pid10 = spawn_link(?MODULE, lserv, [n]), - ?line {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, - {size, {100,No}}, {format, external}]), - ?line {ok, n} = sync_do(Pid10, {open, File, none}), - ?line ok = sync_do(Pid10, block), - ?line {blocked, true} = status(n), - ?line [_] = owners(n), - ?line 1 = users(n), - ?line ok = sync_do(Pid10, close), - ?line ok = status(n), - ?line [_] = owners(n), - ?line 0 = users(n), - ?line ok = disk_log:close(n), - ?line sync_do(Pid10, terminate), - ?line {error, no_such_log} = disk_log:info(n), - ?line true = (P0 == pps()), - - % Blocking user unblocks and closes. - ?line Pid11 = spawn_link(?MODULE, lserv, [n]), - ?line {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, - {size, {100,No}}, {format, external}]), - ?line {ok, n} = sync_do(Pid11, {open, File, none}), - ?line ok = sync_do(Pid11, block), - ?line {blocked, true} = status(n), - ?line [_] = owners(n), - ?line 1 = users(n), - ?line ok = sync_do(Pid11, unblock), - ?line ok = sync_do(Pid11, close), - ?line ok = status(n), - ?line [_] = owners(n), - ?line 0 = users(n), - ?line ok = disk_log:close(n), - ?line {error, no_such_log} = disk_log:info(n), - ?line sync_do(Pid11, terminate), - ?line true = (P0 == pps()), - - % Blocking owner closes. - ?line Pid12 = spawn_link(?MODULE, lserv, [n]), - ?line {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, - {linkto, none}, - {size, {100,No}}, {format, external}]), - ?line {ok, n} = sync_do(Pid12, {open, File}), - ?line ok = sync_do(Pid12, block), - ?line {blocked, true} = status(n), - ?line [_] = owners(n), - ?line 1 = users(n), - ?line ok = sync_do(Pid12, close), - ?line ok = status(n), - ?line [] = owners(n), - ?line 1 = users(n), - ?line ok = disk_log:close(n), - ?line {error, no_such_log} = disk_log:info(n), - ?line sync_do(Pid12, terminate), - ?line true = (P0 == pps()), - - % Blocking owner unblocks and closes. - ?line Pid13 = spawn_link(?MODULE, lserv, [n]), - ?line {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, - {linkto, none}, - {size, {100,No}}, {format, external}]), - ?line {ok, n} = sync_do(Pid13, {open, File}), - ?line ok = sync_do(Pid13, block), - ?line {blocked, true} = status(n), - ?line [_] = owners(n), - ?line 1 = users(n), - ?line ok = sync_do(Pid13, unblock), - ?line ok = sync_do(Pid13, close), - ?line ok = status(n), - ?line [] = owners(n), - ?line 1 = users(n), - ?line ok = disk_log:close(n), - ?line {error, no_such_log} = disk_log:info(n), - ?line sync_do(Pid13, terminate), - ?line true = (P0 == pps()), + Pid8 = spawn_link(?MODULE, lserv, [n]), + Pid9 = spawn_link(?MODULE, lserv, [n]), + {ok, n} = sync_do(Pid8, {open, File}), + {ok, n} = sync_do(Pid9, {open, File}), + ok = sync_do(Pid8, block), + {blocked, true} = status(n), + sync_do(Pid8, terminate), + ok = status(n), + [_] = sync_do(Pid9, owners), + 0 = sync_do(Pid9, users), + sync_do(Pid9, terminate), + {error, no_such_log} = disk_log:info(n), + check_pps(P0), + + %% Blocking user closes. + Pid10 = spawn_link(?MODULE, lserv, [n]), + {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, + {size, {100,No}}, {format, external}]), + {ok, n} = sync_do(Pid10, {open, File, none}), + ok = sync_do(Pid10, block), + {blocked, true} = status(n), + [_] = owners(n), + 1 = users(n), + ok = sync_do(Pid10, close), + ok = status(n), + [_] = owners(n), + 0 = users(n), + ok = disk_log:close(n), + sync_do(Pid10, terminate), + {error, no_such_log} = disk_log:info(n), + check_pps(P0), + + %% Blocking user unblocks and closes. + Pid11 = spawn_link(?MODULE, lserv, [n]), + {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, + {size, {100,No}}, {format, external}]), + {ok, n} = sync_do(Pid11, {open, File, none}), + ok = sync_do(Pid11, block), + {blocked, true} = status(n), + [_] = owners(n), + 1 = users(n), + ok = sync_do(Pid11, unblock), + ok = sync_do(Pid11, close), + ok = status(n), + [_] = owners(n), + 0 = users(n), + ok = disk_log:close(n), + {error, no_such_log} = disk_log:info(n), + sync_do(Pid11, terminate), + check_pps(P0), + + %% Blocking owner closes. + Pid12 = spawn_link(?MODULE, lserv, [n]), + {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, + {linkto, none}, + {size, {100,No}}, {format, external}]), + {ok, n} = sync_do(Pid12, {open, File}), + ok = sync_do(Pid12, block), + {blocked, true} = status(n), + [_] = owners(n), + 1 = users(n), + ok = sync_do(Pid12, close), + ok = status(n), + [] = owners(n), + 1 = users(n), + ok = disk_log:close(n), + {error, no_such_log} = disk_log:info(n), + sync_do(Pid12, terminate), + check_pps(P0), + + %% Blocking owner unblocks and closes. + Pid13 = spawn_link(?MODULE, lserv, [n]), + {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, + {linkto, none}, + {size, {100,No}}, {format, external}]), + {ok, n} = sync_do(Pid13, {open, File}), + ok = sync_do(Pid13, block), + {blocked, true} = status(n), + [_] = owners(n), + 1 = users(n), + ok = sync_do(Pid13, unblock), + ok = sync_do(Pid13, close), + ok = status(n), + [] = owners(n), + 1 = users(n), + ok = disk_log:close(n), + {error, no_such_log} = disk_log:info(n), + sync_do(Pid13, terminate), + check_pps(P0), del(File, No), % cleanup ok. -close_deadlock(suite) -> []; -close_deadlock(doc) -> - ["OTP-4745. Deadlock with just an ordinary log could happen."]; +%% OTP-4745. Deadlock with just an ordinary log could happen. close_deadlock(Conf) when is_list(Conf) -> - ?line true = is_alive(), + true = is_alive(), - ?line PrivDir = ?privdir(Conf), + PrivDir = ?privdir(Conf), - ?line F1 = filename:join(PrivDir, "a.LOG"), - ?line file:delete(F1), + F1 = filename:join(PrivDir, "a.LOG"), + file:delete(F1), Self = self(), %% One process opens the log at the same time as another process @@ -2377,11 +2303,11 @@ close_deadlock(Conf) when is_list(Conf) -> Name = a, Fun = fun() -> open_close(Self, Name, F1) end, P = spawn(Fun), - ?line receive {P, Name} -> ok end, - ?line {ok, L} = disk_log:open([{name,Name},{file,F1}]), - ?line ok = disk_log:close(L), - ?line receive {P, done} -> ok end, - ?line file:delete(F1), + receive {P, Name} -> ok end, + {ok, L} = disk_log:open([{name,Name},{file,F1}]), + ok = disk_log:close(L), + receive {P, done} -> ok end, + file:delete(F1), %% One process opens the log at the same time as another process %% closes the log due to file error while truncating. @@ -2389,38 +2315,38 @@ close_deadlock(Conf) when is_list(Conf) -> %% "work". When it works, as it seems to do right now :), the %% disk_log_server gets {error, no_such_log}, receives the EXIT %% message caused by truncate, and tries to open the log again. - ?line No = 4, - ?line LDir = F1 ++ ".2", - ?line file:del_dir(LDir), - ?line del(F1, No), - ?line ok = file:make_dir(LDir), + No = 4, + LDir = F1 ++ ".2", + file:del_dir(LDir), + del(F1, No), + ok = file:make_dir(LDir), Fun2 = fun() -> open_truncate(Self, Name, F1, No) end, P2 = spawn(Fun2), - ?line receive {P2, Name} -> ok end, - ?line {ok, L} = disk_log:open([{name, Name}, {file, F1}, {type, wrap}, - {format, external}]), + receive {P2, Name} -> ok end, + {ok, L} = disk_log:open([{name, Name}, {file, F1}, {type, wrap}, + {format, external}]), %% Note: truncate causes the disk log process to terminate. One %% cannot say if open above happened before, after, or during the %% termination. The link to the owner is removed before termination. - ?line case disk_log:close(L) of - ok -> ok; - {error,no_such_log} -> - ok - end, - ?line receive {P2, done} -> ok end, - ?line del(F1, No), - ?line file:del_dir(LDir), + case disk_log:close(L) of + ok -> ok; + {error,no_such_log} -> + ok + end, + receive {P2, done} -> ok end, + del(F1, No), + file:del_dir(LDir), %% To the same thing, this time using distributed logs. %% (Does not seem to work very well, unfortunately.) FunD = fun() -> open_close_dist(Self, Name, F1) end, PD = spawn(FunD), receive {PD, Name} -> ok end, - ?line {[_], []} = disk_log:open([{name,Name},{file,F1}, - {distributed,[node()]}]), - ?line ok = disk_log:close(L), + {[_], []} = disk_log:open([{name,Name},{file,F1}, + {distributed,[node()]}]), + ok = disk_log:close(L), receive {PD, done} -> ok end, - ?line file:delete(F1), + file:delete(F1), ok. @@ -2466,7 +2392,7 @@ sync_do(Pid, Req) -> end. lserv(Log) -> - ?line receive + receive {From, {open, File}} -> From ! disk_log:open([{name, Log}, {file, File}, {type, wrap}, {size, {100,1}}, {format, external}]); @@ -2533,664 +2459,662 @@ lserv(Log) -> lserv(Log). -error_repair(suite) -> []; -error_repair(doc) -> - ["Error while repairing."]; +%% Error while repairing. error_repair(Conf) when is_list(Conf) -> - % not all error situations are covered by this test + %% not all error situations are covered by this test DataDir = ?datadir(Conf), PrivDir = ?privdir(Conf), - ?line File = filename:join(PrivDir, "n.LOG"), - ?line No = 4, - ?line file:delete(File), - ?line del(File, No), % cleanup + File = filename:join(PrivDir, "n.LOG"), + No = 4, + file:delete(File), + del(File, No), % cleanup - % kurt.LOG is not closed and has four logged items, one is recovered - ?line copy_wrap_log("kurt.LOG", "n.LOG", No, DataDir, PrivDir), - ?line {repaired,n,{recovered,1},{badbytes,0}} = + %% kurt.LOG is not closed and has four logged items, one is recovered + copy_wrap_log("kurt.LOG", "n.LOG", No, DataDir, PrivDir), + {repaired,n,{recovered,1},{badbytes,0}} = disk_log:open([{name, n}, {file, File}, {type, wrap}, {size,{40,No}}]), - ?line 1 = cur_cnt(n), - ?line 53 = curb(n), - ?line 4 = no_items(n), - ?line ok = disk_log:close(n), - - % temporary repair file cannot be created - ?line copy_wrap_log("kurt.LOG", "n.LOG", No, DataDir, PrivDir), - ?line Dir = File ++ ".4" ++ ".TMP", - ?line ok = file:make_dir(Dir), - ?line P0 = pps(), - ?line {error, {file_error, _, _}} = + 1 = cur_cnt(n), + 53 = curb(n), + 4 = no_items(n), + ok = disk_log:close(n), + + %% temporary repair file cannot be created + copy_wrap_log("kurt.LOG", "n.LOG", No, DataDir, PrivDir), + Dir = File ++ ".4" ++ ".TMP", + ok = file:make_dir(Dir), + P0 = pps(), + {error, {file_error, _, _}} = disk_log:open([{name, n}, {file, File}, {type, wrap}, {size,{40,4}}]), - ?line true = (P0 == pps()), - ?line del(File, No), - ?line ok = file:del_dir(Dir), + check_pps(P0), + del(File, No), + ok = file:del_dir(Dir), + error_logger:add_report_handler(?MODULE, self()), %% repair a file - ?line P1 = pps(), - ?line {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, - {format, internal}, {size, {40,No}}]), - ?line ok = disk_log:log_terms(n, [{this,is}]), % first file full - ?line ok = disk_log:log_terms(n, [{some,terms}]), % second file full - ?line ok = disk_log:close(n), - ?line BadFile = add_ext(File, 2), % current file - ?line set_opened(BadFile), - ?line crash(BadFile, 28), % the binary is now invalid - ?line {repaired,n,{recovered,0},{badbytes,26}} = - disk_log:open([{name, n}, {file, File}, {type, wrap}, - {format, internal}, {size, {40,No}}]), - ?line ok = disk_log:close(n), - ?line true = (P1 == pps()), - ?line del(File, No), + P1 = pps(), + {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, + {format, internal}, {size, {40,No}}]), + ok = disk_log:log_terms(n, [{this,is}]), % first file full + ok = disk_log:log_terms(n, [{some,terms}]), % second file full + ok = disk_log:close(n), + BadFile = add_ext(File, 2), % current file + set_opened(BadFile), + crash(BadFile, 28), % the binary is now invalid + {repaired,n,{recovered,0},{badbytes,26}} = + disk_log:open([{name, n}, {file, File}, {type, wrap}, + {format, internal}, {size, {40,No}}]), + ok = disk_log:close(n), + check_pps(P1), + del(File, No), + receive {info_msg, _, "disk_log: repairing" ++ _, _} -> ok + after 1000 -> ct:fail(failed) end, %% yet another repair - ?line P2 = pps(), - ?line {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, - {format, internal}, {size, {4000,No}}]), - ?line ok = disk_log:log_terms(n, [{this,is},{some,terms}]), - ?line ok = disk_log:close(n), - ?line BadFile2 = add_ext(File, 1), % current file - ?line set_opened(BadFile2), - ?line crash(BadFile2, 51), % the second binary is now invalid - ?line {repaired,n,{recovered,1},{badbytes,26}} = + P2 = pps(), + {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, + {format, internal}, {size, {4000,No}}]), + ok = disk_log:log_terms(n, [{this,is},{some,terms}]), + ok = disk_log:close(n), + BadFile2 = add_ext(File, 1), % current file + set_opened(BadFile2), + crash(BadFile2, 51), % the second binary is now invalid + {repaired,n,{recovered,1},{badbytes,26}} = disk_log:open([{name, n}, {file, File}, {type, wrap}, {format, internal}, {size, {4000,No}}]), - ?line ok = disk_log:close(n), - ?line true = (P2 == pps()), - ?line del(File, No), + ok = disk_log:close(n), + check_pps(P2), + del(File, No), + receive {info_msg, _, "disk_log: repairing" ++ _, _} -> ok + after 1000 -> ct:fail(failed) end, %% Repair, large term - ?line Big = term_to_binary(lists:duplicate(66000,$a)), - ?line {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, - {format, internal}, {size, {40,No}}]), - ?line ok = disk_log:log_terms(n, [Big]), - ?line ok = disk_log:close(n), - ?line set_opened(add_ext(File, 1)), - ?line {repaired,n,{recovered,1},{badbytes,0}} = + Big = term_to_binary(lists:duplicate(66000,$a)), + {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, + {format, internal}, {size, {40,No}}]), + ok = disk_log:log_terms(n, [Big]), + ok = disk_log:close(n), + set_opened(add_ext(File, 1)), + {repaired,n,{recovered,1},{badbytes,0}} = disk_log:open([{name, n}, {file, File}, {type, wrap}, {format, internal}, {size, {40,No}}]), - ?line {_, [Got]} = disk_log:chunk(n, start), - ?line ok = disk_log:close(n), - ?line Got = Big, - ?line del(File, No), + {_, [Got]} = disk_log:chunk(n, start), + ok = disk_log:close(n), + Got = Big, + del(File, No), + receive {info_msg, _, "disk_log: repairing" ++ _, _} -> ok + after 1000 -> ct:fail(failed) end, %% A term a little smaller than a chunk, then big terms. - ?line BigSmall = mk_bytes(1024*64-8-12), - ?line file:delete(File), - ?line {ok, n} = disk_log:open([{name, n}, {file, File}, {type, halt}, - {format, internal}]), - ?line ok = disk_log:log_terms(n, [BigSmall, Big, Big]), - ?line ok = disk_log:close(n), - ?line set_opened(File), - ?line FileSize = file_size(File), - ?line crash(File, FileSize-byte_size(Big)-4), - ?line Error1 = {error, {need_repair, _}} = + BigSmall = mk_bytes(1024*64-8-12), + file:delete(File), + {ok, n} = disk_log:open([{name, n}, {file, File}, {type, halt}, + {format, internal}]), + ok = disk_log:log_terms(n, [BigSmall, Big, Big]), + ok = disk_log:close(n), + set_opened(File), + FileSize = file_size(File), + crash(File, FileSize-byte_size(Big)-4), + Error1 = {error, {need_repair, _}} = disk_log:open([{name, n}, {file, File}, {repair, false}, {type, halt}, {format, internal}]), - ?line "The disk log" ++ _ = format_error(Error1), - ?line {repaired,n,{recovered,2},{badbytes,132013}} = + "The disk log" ++ _ = format_error(Error1), + {repaired,n,{recovered,2},{badbytes,132013}} = disk_log:open([{name, n}, {file, File}, {repair, true}, {type, halt}, {format, internal}]), - ?line ok = disk_log:close(n), - ?line file:delete(File), + ok = disk_log:close(n), + file:delete(File), + receive {info_msg, _, "disk_log: repairing" ++ _, _} -> ok + after 1000 -> ct:fail(failed) end, %% The header is recovered. - ?line {ok,n} = + {ok,n} = disk_log:open([{name, n}, {file, File}, {type, halt}, {format, internal}, {head_func, {?MODULE, head_fun, [{ok,"head"}]}}]), - ?line ok = disk_log:log_terms(n, [list,'of',terms]), - ?line ["head",list,'of',terms] = get_all_terms(n), - ?line ok = disk_log:close(n), - ?line set_opened(File), - ?line crash(File, 30), - ?line {repaired,n,{recovered,3},{badbytes,16}} = + ok = disk_log:log_terms(n, [list,'of',terms]), + ["head",list,'of',terms] = get_all_terms(n), + ok = disk_log:close(n), + set_opened(File), + crash(File, 30), + {repaired,n,{recovered,3},{badbytes,16}} = disk_log:open([{name, n}, {file, File}, {type, halt}, - {format, internal},{repair,true}, + {format, internal},{repair,true}, {quiet, true}, {head_func, {?MODULE, head_fun, [{ok,"head"}]}}]), - ?line ["head",'of',terms] = get_all_terms(n), - ?line ok = disk_log:close(n), - + ["head",'of',terms] = get_all_terms(n), + ok = disk_log:close(n), + error_logger:delete_report_handler(?MODULE), file:delete(File), + {messages, []} = process_info(self(), messages), ok. - + set_opened(File) -> {ok, Fd} = file:open(File, [raw, binary, read, write]), ok = file:write(Fd, [?LOGMAGIC, ?OPENED]), ok = file:close(Fd). -error_log(suite) -> []; -error_log(doc) -> - ["Error while repairing."]; +%% Error while repairing. error_log(Conf) when is_list(Conf) -> - ?line Dir = ?privdir(Conf), - - ?line File = filename:join(Dir, "n.LOG"), - ?line No = 4, - ?line file:delete(File), - ?line del(File, No), % cleanup - ?line LDir = File ++ ".2", - - ?line Q = qlen(), - % dummy just to get all processes "above" disk_log going - ?line {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, - {format, external},{size, {100, No}}]), - ?line ok = disk_log:close(n), - ?line del(File, No), - - % inc_wrap_file fails, the external log is not terminated - ?line P0 = pps(), - ?line {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, - {format, external},{size, {100, No}}]), - ?line ok = file:make_dir(LDir), - ?line {error, {file_error, _, _}} = disk_log:inc_wrap_file(n), - ?line timer:sleep(500), - ?line ok = disk_log:close(n), - ?line del(File, No), - - % inc_wrap_file fails, the internal log is not terminated, ./File.2/ exists - ?line {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, - {format, internal},{size, {100, No}}]), - ?line {error, {file_error, _, _}} = disk_log:inc_wrap_file(n), - ?line ok = disk_log:close(n), - ?line del(File, No), - - % truncate fails, the log is terminated, ./File.2/ exists - ?line {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, - {format, external},{size, {100, No}}]), - ?line {error, {file_error, _, _}} = disk_log:truncate(n), - ?line true = (P0 == pps()), - ?line del(File, No), + Dir = ?privdir(Conf), + + File = filename:join(Dir, "n.LOG"), + No = 4, + file:delete(File), + del(File, No), % cleanup + LDir = File ++ ".2", + + Q = qlen(), + %% dummy just to get all processes "above" disk_log going + {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, + {format, external},{size, {100, No}}]), + ok = disk_log:close(n), + del(File, No), + + %% inc_wrap_file fails, the external log is not terminated + P0 = pps(), + {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, + {format, external},{size, {100, No}}]), + ok = file:make_dir(LDir), + {error, {file_error, _, _}} = disk_log:inc_wrap_file(n), + timer:sleep(500), + ok = disk_log:close(n), + del(File, No), + + %% inc_wrap_file fails, the internal log is not terminated, ./File.2/ exists + {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, + {format, internal},{size, {100, No}}]), + {error, {file_error, _, _}} = disk_log:inc_wrap_file(n), + ok = disk_log:close(n), + del(File, No), + + %% truncate fails, the log is terminated, ./File.2/ exists + {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, + {format, external},{size, {100, No}}]), + {error, {file_error, _, _}} = disk_log:truncate(n), + check_pps(P0), + del(File, No), %% OTP-4880. - % reopen (rename) fails, the log is terminated, ./File.2/ exists - ?line {ok, n} = disk_log:open([{name, n}, {file, File}, {type, halt}, - {format, external},{size, 100000}]), - ?line {error, {file_error, _, eisdir}} = disk_log:reopen(n, LDir), - ?line true = (P0 == pps()), - ?line file:delete(File), + %% reopen (rename) fails, the log is terminated, ./File.2/ exists + {ok, n} = disk_log:open([{name, n}, {file, File}, {type, halt}, + {format, external},{size, 100000}]), + {error, {file_error, _, eisdir}} = disk_log:reopen(n, LDir), + check_pps(P0), + file:delete(File), - ?line B = mk_bytes(60), + B = mk_bytes(60), %% OTP-4880. reopen a wrap log, rename fails - ?line File2 = filename:join(Dir, "n.LOG2"), - ?line {ok, n} = disk_log:open([{name, n}, {file, File2}, {type, wrap}, - {format, external},{size, {100, No}}]), - ?line ok = disk_log:blog_terms(n, [B,B,B]), - ?line {error, {file_error, _, eisdir}} = disk_log:reopen(n, File), - ?line {error, no_such_log} = disk_log:close(n), - ?line del(File2, No), - ?line del(File, No), - - % log, external wrap log, ./File.2/ exists - ?line {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, - {format, external},{size, {100, No}}]), - ?line {error, {file_error, _, _}} = disk_log:blog_terms(n, [B,B,B]), - ?line ok = disk_log:close(n), - ?line del(File, No), - - % log, internal wrap log, ./File.2/ exists - ?line {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, - {format, internal},{size, {100, No}}]), - ?line {error, {file_error, _, _}} = disk_log:log_terms(n, [B,B,B]), - ?line ok = disk_log:close(n), - ?line del(File, No), - - ?line ok = file:del_dir(LDir), - - % can't remove file when changing size - ?line {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, - {format, internal},{size, {100, No}}]), - ?line ok = disk_log:log_terms(n, [B,B,B,B]), - ?line ok = disk_log:change_size(n, {100, No-2}), - ?line Three = File ++ ".3", - ?line ok = file:delete(Three), - ?line ok = file:make_dir(Three), - ?line {error, {file_error, _, _}} = disk_log:log_terms(n, [B,B,B]), - ?line timer:sleep(500), - ?line ok = disk_log:close(n), - ?line ok = file:del_dir(Three), - ?line del(File, No), - ?line Q = qlen(), + File2 = filename:join(Dir, "n.LOG2"), + {ok, n} = disk_log:open([{name, n}, {file, File2}, {type, wrap}, + {format, external},{size, {100, No}}]), + ok = disk_log:blog_terms(n, [B,B,B]), + {error, {file_error, _, eisdir}} = disk_log:reopen(n, File), + {error, no_such_log} = disk_log:close(n), + del(File2, No), + del(File, No), + + %% log, external wrap log, ./File.2/ exists + {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, + {format, external},{size, {100, No}}]), + {error, {file_error, _, _}} = disk_log:blog_terms(n, [B,B,B]), + ok = disk_log:close(n), + del(File, No), + + %% log, internal wrap log, ./File.2/ exists + {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, + {format, internal},{size, {100, No}}]), + {error, {file_error, _, _}} = disk_log:log_terms(n, [B,B,B]), + ok = disk_log:close(n), + del(File, No), + + ok = file:del_dir(LDir), + + %% can't remove file when changing size + {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, + {format, internal},{size, {100, No}}]), + ok = disk_log:log_terms(n, [B,B,B,B]), + ok = disk_log:change_size(n, {100, No-2}), + Three = File ++ ".3", + ok = file:delete(Three), + ok = file:make_dir(Three), + {error, {file_error, _, _}} = disk_log:log_terms(n, [B,B,B]), + timer:sleep(500), + ok = disk_log:close(n), + ok = file:del_dir(Three), + del(File, No), + Q = qlen(), ok. - -chunk(suite) -> []; -chunk(doc) -> - ["Test chunk and chunk_step."]; + +%% Test chunk and chunk_step. chunk(Conf) when is_list(Conf) -> %% See also halt_ro_crash/1 above. Dir = ?privdir(Conf), - ?line File = filename:join(Dir, "n.LOG"), + File = filename:join(Dir, "n.LOG"), No = 4, - ?line B = mk_bytes(60), - ?line BB = mk_bytes(64000), % 64 kB chunks - ?line del(File, No),% cleanup + B = mk_bytes(60), + BB = mk_bytes(64000), % 64 kB chunks + del(File, No),% cleanup %% Make sure chunk_step skips the rest of the binary. %% OTP-3716. This was a bug... - ?line {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, - {format, internal}, {size, {50,No}}]), + {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, + {format, internal}, {size, {50,No}}]), %% 1, 2 and 3 on file one, 4 on file two. - ?line ok = disk_log:log_terms(n, [1,2,3,4]), - ?line {I1, [1]} = disk_log:chunk(n, start, 1), - ?line [{node,Node}] = disk_log:chunk_info(I1), - ?line Node = node(), - ?line Error1 = {error, {no_continuation, foobar}} = + ok = disk_log:log_terms(n, [1,2,3,4]), + {I1, [1]} = disk_log:chunk(n, start, 1), + [{node,Node}] = disk_log:chunk_info(I1), + Node = node(), + Error1 = {error, {no_continuation, foobar}} = disk_log:chunk_info(foobar), - ?line "The term" ++ _ = format_error(Error1), - ?line {ok, I2} = disk_log:chunk_step(n, I1, 1), - ?line {error, {badarg, continuation}} = disk_log:chunk_step(n, foobar, 1), - ?line {I3, [4]} = disk_log:chunk(n, I2, 1), - ?line {ok, I4} = disk_log:chunk_step(n, I3, -1), - ?line {_, [1]} = disk_log:chunk(n, I4, 1), - ?line {error, {badarg, continuation}} = disk_log:bchunk(n, 'begin'), - ?line {Ib1, [Bin1,Bin2]} = disk_log:bchunk(n, start, 2), - ?line 1 = binary_to_term(Bin1), - ?line 2 = binary_to_term(Bin2), - ?line {ok, Ib2} = disk_log:chunk_step(n, Ib1, 1), - ?line {Ib3, [Bin3]} = disk_log:bchunk(n, Ib2, 1), - ?line 4 = binary_to_term(Bin3), - ?line {ok, Ib4} = disk_log:chunk_step(n, Ib3, -1), - ?line {_, [Bin4]} = disk_log:bchunk(n, Ib4, 1), - ?line 1 = binary_to_term(Bin4), - ?line {Ib5, [Bin1, Bin2, Bin17]} = disk_log:bchunk(n, start), - ?line 3 = binary_to_term(Bin17), - ?line {Ib6, [Bin3]} = disk_log:bchunk(n, Ib5, infinity), - ?line eof = disk_log:bchunk(n, Ib6, infinity), - ?line ok = disk_log:close(n), - ?line del(File, No), % cleanup + "The term" ++ _ = format_error(Error1), + {ok, I2} = disk_log:chunk_step(n, I1, 1), + {error, {badarg, continuation}} = disk_log:chunk_step(n, foobar, 1), + {I3, [4]} = disk_log:chunk(n, I2, 1), + {ok, I4} = disk_log:chunk_step(n, I3, -1), + {_, [1]} = disk_log:chunk(n, I4, 1), + {error, {badarg, continuation}} = disk_log:bchunk(n, 'begin'), + {Ib1, [Bin1,Bin2]} = disk_log:bchunk(n, start, 2), + 1 = binary_to_term(Bin1), + 2 = binary_to_term(Bin2), + {ok, Ib2} = disk_log:chunk_step(n, Ib1, 1), + {Ib3, [Bin3]} = disk_log:bchunk(n, Ib2, 1), + 4 = binary_to_term(Bin3), + {ok, Ib4} = disk_log:chunk_step(n, Ib3, -1), + {_, [Bin4]} = disk_log:bchunk(n, Ib4, 1), + 1 = binary_to_term(Bin4), + {Ib5, [Bin1, Bin2, Bin17]} = disk_log:bchunk(n, start), + 3 = binary_to_term(Bin17), + {Ib6, [Bin3]} = disk_log:bchunk(n, Ib5, infinity), + eof = disk_log:bchunk(n, Ib6, infinity), + ok = disk_log:close(n), + del(File, No), % cleanup %% external log, cannot read chunks - ?line {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, - {format, external}, {size, {100,No}}]), - ?line {error, {badarg, continuation}} = disk_log:chunk(n, 'begin'), - ?line {error, {format_external, n}} = disk_log:chunk(n, start), - ?line Error2 = {error, {not_internal_wrap, n}} = + {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, + {format, external}, {size, {100,No}}]), + {error, {badarg, continuation}} = disk_log:chunk(n, 'begin'), + {error, {format_external, n}} = disk_log:chunk(n, start), + Error2 = {error, {not_internal_wrap, n}} = disk_log:chunk_step(n, start, 1), - ?line "The requested" ++ _ = format_error(Error2), - ?line ok = disk_log:close(n), - ?line del(File, No), + "The requested" ++ _ = format_error(Error2), + ok = disk_log:close(n), + del(File, No), %% wrap, read_write - ?line {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, - {format, internal}, {size, {100,No}}]), - ?line ok = disk_log:log_terms(n, [B,B,B,B]), - ?line {C1, [_]} = disk_log:chunk(n, start), - ?line {C2, [_]} = disk_log:chunk(n, C1), - ?line {C3, [_]} = disk_log:chunk(n, C2), - ?line {C4, [_]} = disk_log:chunk(n, C3, 1), - ?line eof = disk_log:chunk(n, C4), - ?line {C5, [_]} = disk_log:chunk(n, start), - ?line {ok, C6} = disk_log:chunk_step(n, C5, 1), - ?line {C7, [_]} = disk_log:chunk(n, C6), - ?line {ok, C8} = disk_log:chunk_step(n, C7, 1), - ?line {_, [_]} = disk_log:chunk(n, C8), - ?line ok = disk_log:close(n), + {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, + {format, internal}, {size, {100,No}}]), + ok = disk_log:log_terms(n, [B,B,B,B]), + {C1, [_]} = disk_log:chunk(n, start), + {C2, [_]} = disk_log:chunk(n, C1), + {C3, [_]} = disk_log:chunk(n, C2), + {C4, [_]} = disk_log:chunk(n, C3, 1), + eof = disk_log:chunk(n, C4), + {C5, [_]} = disk_log:chunk(n, start), + {ok, C6} = disk_log:chunk_step(n, C5, 1), + {C7, [_]} = disk_log:chunk(n, C6), + {ok, C8} = disk_log:chunk_step(n, C7, 1), + {_, [_]} = disk_log:chunk(n, C8), + ok = disk_log:close(n), %% wrap, read_only - ?line {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, - {mode, read_only}, - {format, internal}, {size, {100,No}}]), - ?line {CC1, [_]} = disk_log:chunk(n, start), - ?line {CC2, [_]} = disk_log:chunk(n, CC1), - ?line {CC3, [_]} = disk_log:chunk(n, CC2), - ?line {CC4, [_]} = disk_log:chunk(n, CC3, 1), - ?line eof = disk_log:chunk(n, CC4), - ?line {CC5, [_]} = disk_log:chunk(n, start), - ?line {ok, CC6} = disk_log:chunk_step(n, CC5, 1), - ?line {CC7, [_]} = disk_log:chunk(n, CC6), - ?line {ok, CC8} = disk_log:chunk_step(n, CC7, 1), - ?line {_, [_]} = disk_log:chunk(n, CC8), - ?line ok = disk_log:close(n), + {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, + {mode, read_only}, + {format, internal}, {size, {100,No}}]), + {CC1, [_]} = disk_log:chunk(n, start), + {CC2, [_]} = disk_log:chunk(n, CC1), + {CC3, [_]} = disk_log:chunk(n, CC2), + {CC4, [_]} = disk_log:chunk(n, CC3, 1), + eof = disk_log:chunk(n, CC4), + {CC5, [_]} = disk_log:chunk(n, start), + {ok, CC6} = disk_log:chunk_step(n, CC5, 1), + {CC7, [_]} = disk_log:chunk(n, CC6), + {ok, CC8} = disk_log:chunk_step(n, CC7, 1), + {_, [_]} = disk_log:chunk(n, CC8), + ok = disk_log:close(n), %% OTP-3716. A bug: {Error, List} and {Error, List, Bad} could be %% returned from chunk/2. %% Magic bytes not OK. %% File header (8 bytes) OK, item header not OK. - ?line InvalidFile = add_ext(File, 1), - ?line crash(InvalidFile, 15), - ?line {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, - {mode, read_only}, - {format, internal}, {size, {100,No}}]), - ?line {_, [], 61} = disk_log:chunk(n, start), - ?line ok = disk_log:close(n), + InvalidFile = add_ext(File, 1), + crash(InvalidFile, 15), + {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, + {mode, read_only}, + {format, internal}, {size, {100,No}}]), + {_, [], 61} = disk_log:chunk(n, start), + ok = disk_log:close(n), %% read_write... - ?line {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, - {format, internal}, {size, {100,No}}]), - ?line Error3 = {error, {corrupt_log_file, Culprit}} = + {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, + {format, internal}, {size, {100,No}}]), + Error3 = {error, {corrupt_log_file, Culprit}} = disk_log:chunk(n, start), - ?line "The disk log file" ++ _ = format_error(Error3), - ?line Culprit = InvalidFile, - ?line ok = disk_log:close(n), - ?line del(File, No), + "The disk log file" ++ _ = format_error(Error3), + Culprit = InvalidFile, + ok = disk_log:close(n), + del(File, No), %% Two wrap log files, writing the second one, then reading the first %% one, where a bogus term resides. - ?line {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, - {format, internal}, {size, {40,No}}]), - ?line ok = disk_log:log_terms(n, [{this,is}]), % first file full - ?line ok = disk_log:log_terms(n, [{some,terms}]), % second file full - ?line 2 = curf(n), - ?line BadFile = add_ext(File, 1), - ?line crash(BadFile, 28), % the _binary_ is now invalid - ?line {error, {corrupt_log_file, BFile}} = disk_log:chunk(n, start, 1), - ?line BadFile = BFile, - ?line ok = disk_log:close(n), + {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, + {format, internal}, {size, {40,No}}]), + ok = disk_log:log_terms(n, [{this,is}]), % first file full + ok = disk_log:log_terms(n, [{some,terms}]), % second file full + 2 = curf(n), + BadFile = add_ext(File, 1), + crash(BadFile, 28), % the _binary_ is now invalid + {error, {corrupt_log_file, BFile}} = disk_log:chunk(n, start, 1), + BadFile = BFile, + ok = disk_log:close(n), %% The same, with a halt log. - ?line file:delete(File), % cleanup - ?line {ok, n} = disk_log:open([{name, n}, {file, File}, {type, halt}, - {format, internal}]), - ?line ok = disk_log:log_terms(n, [{this,is}]), - ?line ok = disk_log:sync(n), - ?line crash(File, 28), % the _binary_ is now invalid - ?line {error, {corrupt_log_file, File2}} = disk_log:chunk(n, start, 1), - ?line crash(File, 10), - ?line {error,{corrupt_log_file,_}} = disk_log:bchunk(n, start, 1), - ?line true = File == File2, - ?line ok = disk_log:close(n), - ?line del(File, No), + file:delete(File), % cleanup + {ok, n} = disk_log:open([{name, n}, {file, File}, {type, halt}, + {format, internal}]), + ok = disk_log:log_terms(n, [{this,is}]), + ok = disk_log:sync(n), + crash(File, 28), % the _binary_ is now invalid + {error, {corrupt_log_file, File2}} = disk_log:chunk(n, start, 1), + crash(File, 10), + {error,{corrupt_log_file,_}} = disk_log:bchunk(n, start, 1), + true = File == File2, + ok = disk_log:close(n), + del(File, No), %% halt, read_write - ?line file:delete(File), % cleanup - ?line {ok, n} = disk_log:open([{name, n}, {file, File}, {type, halt}, - {format, internal}]), - ?line ok = disk_log:log_terms(n, [BB,BB,BB,BB]), - ?line {D1, [Ch1]} = disk_log:chunk(n, start, 1), - ?line Ch1 = BB, - ?line {D2, [Ch2]} = disk_log:chunk(n, D1, 1), - ?line Ch2 = BB, - ?line {D3, [Ch3]} = disk_log:chunk(n, D2, 1), - ?line Ch3 = BB, - ?line {D4, [Ch4]} = disk_log:chunk(n, D3, 1), - ?line Ch4 = BB, - ?line eof = disk_log:chunk(n, D4), - ?line ok = disk_log:close(n), + file:delete(File), % cleanup + {ok, n} = disk_log:open([{name, n}, {file, File}, {type, halt}, + {format, internal}]), + ok = disk_log:log_terms(n, [BB,BB,BB,BB]), + {D1, [Ch1]} = disk_log:chunk(n, start, 1), + Ch1 = BB, + {D2, [Ch2]} = disk_log:chunk(n, D1, 1), + Ch2 = BB, + {D3, [Ch3]} = disk_log:chunk(n, D2, 1), + Ch3 = BB, + {D4, [Ch4]} = disk_log:chunk(n, D3, 1), + Ch4 = BB, + eof = disk_log:chunk(n, D4), + ok = disk_log:close(n), %% halt, read_only - ?line {ok, n} = disk_log:open([{name, n}, {file, File}, {type, halt}, - {format, internal},{mode,read_only}]), - ?line {E1, [Ch5]} = disk_log:chunk(n, start, 1), - ?line Ch5 = BB, - ?line {E2, [Ch6]} = disk_log:chunk(n, E1, 1), - ?line Ch6 = BB, - ?line {E3, [Ch7]} = disk_log:chunk(n, E2, 1), - ?line Ch7 = BB, - ?line {E4, [Ch8]} = disk_log:chunk(n, E3, 1), - ?line Ch8 = BB, - ?line eof = disk_log:chunk(n, E4), - ?line ok = disk_log:close(n), - ?line file:delete(File), % cleanup + {ok, n} = disk_log:open([{name, n}, {file, File}, {type, halt}, + {format, internal},{mode,read_only}]), + {E1, [Ch5]} = disk_log:chunk(n, start, 1), + Ch5 = BB, + {E2, [Ch6]} = disk_log:chunk(n, E1, 1), + Ch6 = BB, + {E3, [Ch7]} = disk_log:chunk(n, E2, 1), + Ch7 = BB, + {E4, [Ch8]} = disk_log:chunk(n, E3, 1), + Ch8 = BB, + eof = disk_log:chunk(n, E4), + ok = disk_log:close(n), + file:delete(File), % cleanup %% More than 64 kB term. - ?line BBB = term_to_binary(lists:duplicate(66000,$a)), - ?line {ok, n} = disk_log:open([{name, n}, {file, File}, {type, halt}, - {format, internal}]), - ?line ok = disk_log:log_terms(n, [BBB]), - ?line {F1, [BBB1]} = disk_log:chunk(n, start), - ?line BBB1 = BBB, - ?line eof = disk_log:chunk(n, F1), - ?line ok = disk_log:close(n), - ?line {ok, n} = disk_log:open([{name, n}, {file, File}, {type, halt}, - {format, internal}, {mode, read_only}]), - ?line {F1r, [BBB2]} = disk_log:chunk(n, start), - ?line BBB2 = BBB, - ?line eof = disk_log:chunk(n, F1r), - ?line ok = disk_log:close(n), - - ?line truncate(File, 8192), - ?line {ok, n} = disk_log:open([{name, n}, {file, File}, {type, halt}, - {format, internal}]), - ?line {error, {corrupt_log_file, _}} = disk_log:chunk(n, start), - ?line ok = disk_log:close(n), - ?line {ok, n} = disk_log:open([{name, n}, {file, File}, {type, halt}, - {format, internal}, {mode, read_only}]), - ?line {K1, [], 8176} = disk_log:chunk(n, start), - ?line eof = disk_log:chunk(n, K1), - ?line ok = disk_log:close(n), - ?line file:delete(File), % cleanup + BBB = term_to_binary(lists:duplicate(66000,$a)), + {ok, n} = disk_log:open([{name, n}, {file, File}, {type, halt}, + {format, internal}]), + ok = disk_log:log_terms(n, [BBB]), + {F1, [BBB1]} = disk_log:chunk(n, start), + BBB1 = BBB, + eof = disk_log:chunk(n, F1), + ok = disk_log:close(n), + {ok, n} = disk_log:open([{name, n}, {file, File}, {type, halt}, + {format, internal}, {mode, read_only}]), + {F1r, [BBB2]} = disk_log:chunk(n, start), + BBB2 = BBB, + eof = disk_log:chunk(n, F1r), + ok = disk_log:close(n), + + truncate(File, 8192), + {ok, n} = disk_log:open([{name, n}, {file, File}, {type, halt}, + {format, internal}]), + {error, {corrupt_log_file, _}} = disk_log:chunk(n, start), + ok = disk_log:close(n), + {ok, n} = disk_log:open([{name, n}, {file, File}, {type, halt}, + {format, internal}, {mode, read_only}]), + {K1, [], 8176} = disk_log:chunk(n, start), + eof = disk_log:chunk(n, K1), + ok = disk_log:close(n), + file:delete(File), % cleanup %% OTP-3716. A bug: eof in the middle of the last element is not ok. - ?line {ok, n} = disk_log:open([{name, n}, {file, File}, {type, halt}, - {format, internal}]), - ?line ok = disk_log:log_terms(n, [B,BB]), - ?line ok = disk_log:close(n), - ?line truncate(File, 80), - ?line {ok, n} = disk_log:open([{name, n}, {file, File}, {type, halt}, - {format, internal}]), - ?line {G1, [_]} = disk_log:chunk(n, start, 1), - ?line {error, {corrupt_log_file, _}} = disk_log:chunk(n, G1, 1), - ?line ok = disk_log:close(n), - ?line {ok, n} = disk_log:open([{name, n}, {file, File}, {type, halt}, - {format, internal}, {mode, read_only}]), - ?line {G1r, [_]} = disk_log:chunk(n, start, 1), - ?line {_, [], 4} = disk_log:chunk(n, G1r, 1), - ?line ok = disk_log:close(n), - ?line file:delete(File), % cleanup + {ok, n} = disk_log:open([{name, n}, {file, File}, {type, halt}, + {format, internal}]), + ok = disk_log:log_terms(n, [B,BB]), + ok = disk_log:close(n), + truncate(File, 80), + {ok, n} = disk_log:open([{name, n}, {file, File}, {type, halt}, + {format, internal}]), + {G1, [_]} = disk_log:chunk(n, start, 1), + {error, {corrupt_log_file, _}} = disk_log:chunk(n, G1, 1), + ok = disk_log:close(n), + {ok, n} = disk_log:open([{name, n}, {file, File}, {type, halt}, + {format, internal}, {mode, read_only}]), + {G1r, [_]} = disk_log:chunk(n, start, 1), + {_, [], 4} = disk_log:chunk(n, G1r, 1), + ok = disk_log:close(n), + file:delete(File), % cleanup %% Opening a wrap log read-only. The second of four terms is destroyed. - ?line {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, - {format, internal}, {size, {4000,No}}]), - ?line ok = disk_log:log_terms(n, - [{this,is},{some,terms},{on,a},{wrap,file}]), - ?line ok = disk_log:close(n), - ?line {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, - {format, internal}, {mode, read_only}]), - ?line CrashFile = add_ext(File, 1), - ?line crash(CrashFile, 51), % the binary term {some,terms} is now bad - ?line {H1, [{this,is}], 18} = disk_log:chunk(n, start, 10), - ?line {H2, [{on,a},{wrap,file}]} = disk_log:chunk(n, H1), - ?line eof = disk_log:chunk(n, H2), - ?line ok = disk_log:close(n), - ?line del(File, No), + {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, + {format, internal}, {size, {4000,No}}]), + ok = disk_log:log_terms(n, + [{this,is},{some,terms},{on,a},{wrap,file}]), + ok = disk_log:close(n), + {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, + {format, internal}, {mode, read_only}]), + CrashFile = add_ext(File, 1), + crash(CrashFile, 51), % the binary term {some,terms} is now bad + {H1, [{this,is}], 18} = disk_log:chunk(n, start, 10), + {H2, [{on,a},{wrap,file}]} = disk_log:chunk(n, H1), + eof = disk_log:chunk(n, H2), + ok = disk_log:close(n), + del(File, No), %% The same as last, but with a halt log. - ?line {ok, n} = disk_log:open([{name, n}, {file, File}, {type, halt}, - {format, internal}, {mode, read_write}]), - ?line ok = disk_log:alog_terms(n, [{this,is},{some,terms}]), - ?line ok = disk_log:log_terms(n, [{on,a},{halt,file}]), - ?line ok = disk_log:close(n), - ?line {ok, n} = disk_log:open([{name, n}, {file, File}, {type, halt}, - {format, internal}, {mode, read_only}]), - ?line crash(File, 51), % the binary term {some,terms} is now bad - ?line {J1, [{this,is}], 18} = disk_log:chunk(n, start, 10), - ?line {J2, [{on,a},{halt,file}]} = disk_log:chunk(n, J1), - ?line eof = disk_log:chunk(n, J2), - ?line ok = disk_log:close(n), - ?line file:delete(File), + {ok, n} = disk_log:open([{name, n}, {file, File}, {type, halt}, + {format, internal}, {mode, read_write}]), + ok = disk_log:alog_terms(n, [{this,is},{some,terms}]), + ok = disk_log:log_terms(n, [{on,a},{halt,file}]), + ok = disk_log:close(n), + {ok, n} = disk_log:open([{name, n}, {file, File}, {type, halt}, + {format, internal}, {mode, read_only}]), + crash(File, 51), % the binary term {some,terms} is now bad + {J1, [{this,is}], 18} = disk_log:chunk(n, start, 10), + {J2, [{on,a},{halt,file}]} = disk_log:chunk(n, J1), + eof = disk_log:chunk(n, J2), + ok = disk_log:close(n), + file:delete(File), %% OTP-7641. Same as last one, but the size of the bad term is %% less than ?HEADERSz (8) bytes. - ?line {ok, n} = disk_log:open([{name, n}, {file, File}, {type, halt}, - {format, internal}, {mode, read_write}]), - ?line ok = disk_log:alog_terms(n, [{this,is},{s}]), - ?line ok = disk_log:log_terms(n, [{on,a},{halt,file}]), - ?line ok = disk_log:close(n), - ?line {ok, n} = disk_log:open([{name, n}, {file, File}, {type, halt}, - {format, internal}, {mode, read_only}]), - ?line crash(File, 44), % the binary term {s} is now bad - ?line {J11, [{this,is}], 7} = disk_log:chunk(n, start, 10), - ?line {J21, [{on,a},{halt,file}]} = disk_log:chunk(n, J11), - ?line eof = disk_log:chunk(n, J21), - ?line ok = disk_log:close(n), - ?line file:delete(File), + {ok, n} = disk_log:open([{name, n}, {file, File}, {type, halt}, + {format, internal}, {mode, read_write}]), + ok = disk_log:alog_terms(n, [{this,is},{s}]), + ok = disk_log:log_terms(n, [{on,a},{halt,file}]), + ok = disk_log:close(n), + {ok, n} = disk_log:open([{name, n}, {file, File}, {type, halt}, + {format, internal}, {mode, read_only}]), + crash(File, 44), % the binary term {s} is now bad + {J11, [{this,is}], 7} = disk_log:chunk(n, start, 10), + {J21, [{on,a},{halt,file}]} = disk_log:chunk(n, J11), + eof = disk_log:chunk(n, J21), + ok = disk_log:close(n), + file:delete(File), %% Minimal MD5-proctected term, and maximal unprotected term. %% A chunk ends in the middle of the MD5-sum. - ?line MD5term = mk_bytes(64*1024-8), - ?line NotMD5term = mk_bytes((64*1024-8)-1), - ?line Term2 = mk_bytes((64*1024-8)-16), - ?line MD5L = [MD5term,NotMD5term,Term2,MD5term,MD5term,NotMD5term], - ?line {ok, n} = disk_log:open([{name, n}, {file, File}, {type, halt}, - {format, internal}]), - ?line ok = disk_log:log_terms(n, MD5L), - ?line true = MD5L == get_all_terms(n), - ?line ok = disk_log:close(n), - ?line true = MD5L == get_all_terms(n, File, halt), - ?line crash(File, 21), % the MD5-sum of the first term is now bad - ?line true = {tl(MD5L),64*1024-8} == get_all_terms_and_bad(n, File, halt), - ?line {_,64*1024-8} = get_all_binary_terms_and_bad(n, File, halt), - ?line {ok, n} = disk_log:open([{name, n}, {file, File}, {type, halt}, - {format, internal}]), - ?line {error, {corrupt_log_file, _}} = disk_log:chunk(n, start), - ?line ok = disk_log:close(n), - ?line file:delete(File), + MD5term = mk_bytes(64*1024-8), + NotMD5term = mk_bytes((64*1024-8)-1), + Term2 = mk_bytes((64*1024-8)-16), + MD5L = [MD5term,NotMD5term,Term2,MD5term,MD5term,NotMD5term], + {ok, n} = disk_log:open([{name, n}, {file, File}, {type, halt}, + {format, internal}]), + ok = disk_log:log_terms(n, MD5L), + true = MD5L == get_all_terms(n), + ok = disk_log:close(n), + true = MD5L == get_all_terms(n, File, halt), + crash(File, 21), % the MD5-sum of the first term is now bad + true = {tl(MD5L),64*1024-8} == get_all_terms_and_bad(n, File, halt), + {_,64*1024-8} = get_all_binary_terms_and_bad(n, File, halt), + {ok, n} = disk_log:open([{name, n}, {file, File}, {type, halt}, + {format, internal}]), + {error, {corrupt_log_file, _}} = disk_log:chunk(n, start), + ok = disk_log:close(n), + file:delete(File), %% A file with "old" terms (magic word is MAGICINT). DataDir = ?datadir(Conf), OldTermsFileOrig = filename:join(DataDir, "old_terms.LOG"), OldTermsFile = filename:join(Dir, "old_terms.LOG"), - ?line copy_file(OldTermsFileOrig, OldTermsFile), - ?line {[_,_,_,_],0} = get_all_terms_and_bad(n, OldTermsFile, halt), - ?line {ok, n} = disk_log:open([{name, n}, {file, OldTermsFile}, - {type, halt}, {format, internal}]), - ?line [_,_,_,_] = get_all_terms(n), - ?line ok = disk_log:close(n), - ?line file:delete(OldTermsFile), + copy_file(OldTermsFileOrig, OldTermsFile), + {[_,_,_,_],0} = get_all_terms_and_bad(n, OldTermsFile, halt), + {ok, n} = disk_log:open([{name, n}, {file, OldTermsFile}, + {type, halt}, {format, internal}]), + [_,_,_,_] = get_all_terms(n), + ok = disk_log:close(n), + file:delete(OldTermsFile), ok. -error_index(suite) -> []; -error_index(doc) -> - ["OTP-5558. Keep the contents of index files after disk crash."]; +%% OTP-5558. Keep the contents of index files after disk crash. error_index(Conf) when is_list(Conf) -> - ?line Dir = ?privdir(Conf), + Dir = ?privdir(Conf), - ?line File = filename:join(Dir, "n.LOG"), - ?line IdxFile = File ++ ".idx", - ?line No = 4, - ?line file:delete(File), - ?line del(File, No), % cleanup + File = filename:join(Dir, "n.LOG"), + IdxFile = File ++ ".idx", + No = 4, + file:delete(File), + del(File, No), % cleanup Args = [{name,n},{type,wrap},{size,{100,No}},{file,File}], - ?line {ok, n} = disk_log:open(Args), - ?line ok = disk_log:close(n), - ?line Q = qlen(), + {ok, n} = disk_log:open(Args), + ok = disk_log:close(n), + Q = qlen(), P0 = pps(), - ?line ok = file:write_file(IdxFile, <<"abc">>), - ?line {error, {invalid_index_file, _}} = disk_log:open(Args), - ?line {error, {invalid_index_file, _}} = disk_log:open(Args), - ?line {error, {invalid_index_file, _}} = disk_log:open(Args), - - ?line del(File, No), - ?line true = (P0 == pps()), - ?line true = (Q == qlen()), + ok = file:write_file(IdxFile, <<"abc">>), + {error, {invalid_index_file, _}} = disk_log:open(Args), + {error, {invalid_index_file, _}} = disk_log:open(Args), + {error, {invalid_index_file, _}} = disk_log:open(Args), + + del(File, No), + check_pps(P0), + true = (Q == qlen()), ok. - -truncate(suite) -> []; -truncate(doc) -> - ["Test truncate/1 on halt and wrap logs."]; + +%% Test truncate/1 on halt and wrap logs. truncate(Conf) when is_list(Conf) -> Dir = ?privdir(Conf), - ?line Q = qlen(), + Q = qlen(), Halt = join(Dir, "halt.LOG"), - % Halt logs. - - ?line file:delete(Halt), % cleanup - ?line {ok, halt} = disk_log:open([{name, halt}, {type, halt}, {file, Halt}, - {head, header}, {notify, true}]), - ?line infinity = sz(halt), - ?line ok = disk_log:truncate(halt, tjohej), - ?line rec(1, {disk_log, node(), halt, {truncated, 1}}), - ?line ok = disk_log:change_size(halt, 10000), - ?line 10000 = sz(halt), - ?line disk_log:close(halt), - ?line [tjohej] = get_all_terms(halt, Halt, halt), - ?line file:delete(Halt), - - ?line {ok, halt} = disk_log:open([{name, halt}, {type, halt}, {file, Halt}, - {head, header}, {notify, true}]), - ?line ok = disk_log:truncate(halt), - ?line rec(1, {disk_log, node(), halt, {truncated, 1}}), - ?line disk_log:close(halt), - ?line [header] = get_all_terms(halt, Halt, halt), - ?line file:delete(Halt), - - ?line {ok, halt} = disk_log:open([{name, halt}, {type, halt}, - {file, Halt}, {format, external}, - {head, "header"}, {notify, false}]), - ?line ok = disk_log:btruncate(halt, "apa"), - ?line disk_log:close(halt), - ?line 3 = file_size(Halt), - ?line file:delete(Halt), - + %% Halt logs. + + file:delete(Halt), % cleanup + {ok, halt} = disk_log:open([{name, halt}, {type, halt}, {file, Halt}, + {head, header}, {notify, true}]), + infinity = sz(halt), + ok = disk_log:truncate(halt, tjohej), + rec(1, {disk_log, node(), halt, {truncated, 1}}), + ok = disk_log:change_size(halt, 10000), + 10000 = sz(halt), + disk_log:close(halt), + [tjohej] = get_all_terms(halt, Halt, halt), + file:delete(Halt), + + {ok, halt} = disk_log:open([{name, halt}, {type, halt}, {file, Halt}, + {head, header}, {notify, true}]), + ok = disk_log:truncate(halt), + rec(1, {disk_log, node(), halt, {truncated, 1}}), + disk_log:close(halt), + [header] = get_all_terms(halt, Halt, halt), + file:delete(Halt), + + {ok, halt} = disk_log:open([{name, halt}, {type, halt}, + {file, Halt}, {format, external}, + {head, "header"}, {notify, false}]), + ok = disk_log:btruncate(halt, "apa"), + disk_log:close(halt), + 3 = file_size(Halt), + file:delete(Halt), + %% Wrap logs. - ?line File = filename:join(Dir, "n.LOG"), - ?line No = 4, - ?line B = mk_bytes(60), - ?line del(File, No), % cleanup + File = filename:join(Dir, "n.LOG"), + No = 4, + B = mk_bytes(60), + del(File, No), % cleanup %% Internal with header. - ?line Size = {100, No}, - ?line {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, - {head, header}, {notify, true}, - {size, Size}]), - ?line ok = disk_log:log_terms(n, [B,B,B]), + Size = {100, No}, + {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, + {head, header}, {notify, true}, + {size, Size}]), + ok = disk_log:log_terms(n, [B,B,B]), %% Used to be one message, but now one per wrapped file. - ?line rec(2, {disk_log, node(), n, {wrap, 0}}), - ?line ok = disk_log:truncate(n, apa), - ?line rec(1, {disk_log, node(), n, {truncated, 6}}), - ?line {0, 0} = no_overflows(n), - ?line 23 = curb(n), - ?line 1 = curf(n), - ?line 1 = cur_cnt(n), - ?line true = (Size == sz(n)), - - ?line ok = disk_log:log_terms(n, [B, B]), - ?line rec(1, {disk_log, node(), n, {wrap, 0}}), - ?line ok = disk_log:close(n), - ?line [apa, _, header, _] = get_all_terms(n, File, wrap), - ?line del(File, No), - + rec(2, {disk_log, node(), n, {wrap, 0}}), + ok = disk_log:truncate(n, apa), + rec(1, {disk_log, node(), n, {truncated, 6}}), + {0, 0} = no_overflows(n), + 23 = curb(n), + 1 = curf(n), + 1 = cur_cnt(n), + true = (Size == sz(n)), + + ok = disk_log:log_terms(n, [B, B]), + rec(1, {disk_log, node(), n, {wrap, 0}}), + ok = disk_log:close(n), + [apa, _, header, _] = get_all_terms(n, File, wrap), + del(File, No), + %% Internal without general header. - ?line {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, - {notify, true}, - {size, {100, No}}]), - ?line ok = disk_log:log_terms(n, [B,B,B]), - ?line rec(2, {disk_log, node(), n, {wrap, 0}}), - ?line ok = disk_log:truncate(n, apa), - ?line rec(1, {disk_log, node(), n, {truncated, 3}}), - ?line {0, 0} = no_overflows(n), - ?line 23 = curb(n), - ?line 1 = curf(n), - ?line 1 = cur_cnt(n), - ?line true = (Size == sz(n)), - - ?line ok = disk_log:log_terms(n, [B, B]), - ?line rec(1, {disk_log, node(), n, {wrap, 0}}), - ?line ok = disk_log:close(n), - ?line [apa, _, _] = get_all_terms(n, File, wrap), - ?line del(File, No), + {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, + {notify, true}, + {size, {100, No}}]), + ok = disk_log:log_terms(n, [B,B,B]), + rec(2, {disk_log, node(), n, {wrap, 0}}), + ok = disk_log:truncate(n, apa), + rec(1, {disk_log, node(), n, {truncated, 3}}), + {0, 0} = no_overflows(n), + 23 = curb(n), + 1 = curf(n), + 1 = cur_cnt(n), + true = (Size == sz(n)), + + ok = disk_log:log_terms(n, [B, B]), + rec(1, {disk_log, node(), n, {wrap, 0}}), + ok = disk_log:close(n), + [apa, _, _] = get_all_terms(n, File, wrap), + del(File, No), %% Internal without any header. - ?line {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, - {notify, true}, - {size, {100, No}}]), - ?line ok = disk_log:log_terms(n, [B,B,B]), - ?line rec(2, {disk_log, node(), n, {wrap, 0}}), - ?line ok = disk_log:truncate(n), - ?line rec(1, {disk_log, node(), n, {truncated, 3}}), - ?line {0, 0} = no_overflows(n), - ?line 8 = curb(n), - ?line 1 = curf(n), - ?line 0 = cur_cnt(n), - ?line true = (Size == sz(n)), - - ?line ok = disk_log:log_terms(n, [B, B]), - ?line rec(1, {disk_log, node(), n, {wrap, 0}}), - ?line ok = disk_log:close(n), - ?line [_, _] = get_all_terms(n, File, wrap), - ?line del(File, No), - ?line Q = qlen(), + {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, + {notify, true}, + {size, {100, No}}]), + ok = disk_log:log_terms(n, [B,B,B]), + rec(2, {disk_log, node(), n, {wrap, 0}}), + ok = disk_log:truncate(n), + rec(1, {disk_log, node(), n, {truncated, 3}}), + {0, 0} = no_overflows(n), + 8 = curb(n), + 1 = curf(n), + 0 = cur_cnt(n), + true = (Size == sz(n)), + + ok = disk_log:log_terms(n, [B, B]), + rec(1, {disk_log, node(), n, {wrap, 0}}), + ok = disk_log:close(n), + [_, _] = get_all_terms(n, File, wrap), + del(File, No), + Q = qlen(), ok. -many_users(suite) -> []; -many_users(doc) -> - ["Test many users logging and sync:ing at the same time."]; +%% Test many users logging and sync:ing at the same time. many_users(Conf) when is_list(Conf) -> Dir = ?privdir(Conf), N = 100, @@ -3198,32 +3122,32 @@ many_users(Conf) when is_list(Conf) -> Fun1 = fun(Name, Pid, I) -> disk_log:log(Name, {Pid, I}) end, Fun2 = fun(Name, Pid, I) -> ok = disk_log:log(Name, {Pid, I}), disk_log:sync(Name) end, - ?line {C1, T1} = many(Fun2, NoClients, N, halt, internal, infinity, Dir), - ?line true = lists:duplicate(NoClients, ok) == C1, - ?line true = length(T1) == N*NoClients, - ?line {C2, T2} = many(Fun1, NoClients, N, halt, internal, 1000, Dir), - ?line true = lists:duplicate(NoClients, {error, {full,"log.LOG"}}) == C2, - ?line true = length(T2) > 0, - ?line {C3, T3} = many(Fun2, NoClients, N, wrap, internal, - {300*NoClients,200}, Dir), - ?line true = lists:duplicate(NoClients, ok) == C3, - ?line true = length(T3) == N*NoClients, + {C1, T1} = many(Fun2, NoClients, N, halt, internal, infinity, Dir), + true = lists:duplicate(NoClients, ok) == C1, + true = length(T1) == N*NoClients, + {C2, T2} = many(Fun1, NoClients, N, halt, internal, 1000, Dir), + true = lists:duplicate(NoClients, {error, {full,"log.LOG"}}) == C2, + true = length(T2) > 0, + {C3, T3} = many(Fun2, NoClients, N, wrap, internal, + {300*NoClients,200}, Dir), + true = lists:duplicate(NoClients, ok) == C3, + true = length(T3) == N*NoClients, ok. many(Fun, NoClients, N, Type, Format, Size, Dir) -> Name = "log.LOG", File = filename:join(Dir, Name), del_files(Size, File), - ?line Q = qlen(), - ?line {ok, _} = disk_log:open([{name,Name}, {type,Type}, {size,Size}, - {format,Format}, {file,File}]), - ?line Pids = spawn_clients(NoClients, client, [self(), Name, N, Fun]), - ?line Checked = check_clients(Pids), - ?line ok = disk_log:close(Name), - ?line Terms = get_all_terms(Name, File, Type), - ?line del_files(Size, File), - ?line Q = qlen(), - ?line {Checked, Terms}. + Q = qlen(), + {ok, _} = disk_log:open([{name,Name}, {type,Type}, {size,Size}, + {format,Format}, {file,File}]), + Pids = spawn_clients(NoClients, client, [self(), Name, N, Fun]), + Checked = check_clients(Pids), + ok = disk_log:close(Name), + Terms = get_all_terms(Name, File, Type), + del_files(Size, File), + Q = qlen(), + {Checked, Terms}. spawn_clients(0, _F, _A) -> []; @@ -3250,211 +3174,208 @@ del_files(_Size, File) -> -info_current(suite) -> []; -info_current(doc) -> - ["Test no_current_{bytes, items} as returned by info/0."]; +%% Test no_current_{bytes, items} as returned by info/0. info_current(Conf) when is_list(Conf) -> Dir = ?privdir(Conf), - ?line File = filename:join(Dir, "n.LOG"), + File = filename:join(Dir, "n.LOG"), No = 4, B = mk_bytes(60), BB = mk_bytes(160), % bigger than a single wrap log file SB = mk_bytes(10), % much smaller than a single wrap log file - ?line del(File, No),% cleanup + del(File, No),% cleanup - ?line Q = qlen(), + Q = qlen(), %% Internal with header. - ?line {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, - {head, header}, {size, {100,No}}]), - ?line {26, 1} = {curb(n), cur_cnt(n)}, - ?line {1, 1} = {no_written_items(n), no_items(n)}, - ?line ok = disk_log:log(n, B), - ?line {94, 2} = {curb(n), cur_cnt(n)}, - ?line {2, 2} = {no_written_items(n), no_items(n)}, - ?line ok = disk_log:close(n), - ?line {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, - {notify, true}, - {head, header}, {size, {100,No}}]), - ?line {94, 2} = {curb(n), cur_cnt(n)}, - ?line {0, 2} = {no_written_items(n), no_items(n)}, - ?line ok = disk_log:log(n, B), - ?line rec(1, {disk_log, node(), n, {wrap, 0}}), - ?line {94, 2} = {curb(n), cur_cnt(n)}, - ?line {2, 4} = {no_written_items(n), no_items(n)}, - ?line disk_log:inc_wrap_file(n), - ?line rec(1, {disk_log, node(), n, {wrap, 0}}), - ?line {26, 1} = {curb(n), cur_cnt(n)}, - ?line {3, 4} = {no_written_items(n), no_items(n)}, - ?line ok = disk_log:log_terms(n, [B,B,B]), + {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, + {head, header}, {size, {100,No}}]), + {26, 1} = {curb(n), cur_cnt(n)}, + {1, 1} = {no_written_items(n), no_items(n)}, + ok = disk_log:log(n, B), + {94, 2} = {curb(n), cur_cnt(n)}, + {2, 2} = {no_written_items(n), no_items(n)}, + ok = disk_log:close(n), + {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, + {notify, true}, + {head, header}, {size, {100,No}}]), + {94, 2} = {curb(n), cur_cnt(n)}, + {0, 2} = {no_written_items(n), no_items(n)}, + ok = disk_log:log(n, B), + rec(1, {disk_log, node(), n, {wrap, 0}}), + {94, 2} = {curb(n), cur_cnt(n)}, + {2, 4} = {no_written_items(n), no_items(n)}, + disk_log:inc_wrap_file(n), + rec(1, {disk_log, node(), n, {wrap, 0}}), + {26, 1} = {curb(n), cur_cnt(n)}, + {3, 4} = {no_written_items(n), no_items(n)}, + ok = disk_log:log_terms(n, [B,B,B]), %% Used to be one message, but now one per wrapped file. - ?line rec(1, {disk_log, node(), n, {wrap, 0}}), - ?line rec(1, {disk_log, node(), n, {wrap, 2}}), - ?line {94, 2} = {curb(n), cur_cnt(n)}, - ?line {8, 7} = {no_written_items(n), no_items(n)}, - ?line ok = disk_log:log_terms(n, [B]), - ?line rec(1, {disk_log, node(), n, {wrap, 2}}), - ?line ok = disk_log:log_terms(n, [B]), - ?line rec(1, {disk_log, node(), n, {wrap, 2}}), - ?line {94, 2} = {curb(n), cur_cnt(n)}, - ?line {12, 7} = {no_written_items(n), no_items(n)}, - ?line ok = disk_log:log_terms(n, [BB,BB]), + rec(1, {disk_log, node(), n, {wrap, 0}}), + rec(1, {disk_log, node(), n, {wrap, 2}}), + {94, 2} = {curb(n), cur_cnt(n)}, + {8, 7} = {no_written_items(n), no_items(n)}, + ok = disk_log:log_terms(n, [B]), + rec(1, {disk_log, node(), n, {wrap, 2}}), + ok = disk_log:log_terms(n, [B]), + rec(1, {disk_log, node(), n, {wrap, 2}}), + {94, 2} = {curb(n), cur_cnt(n)}, + {12, 7} = {no_written_items(n), no_items(n)}, + ok = disk_log:log_terms(n, [BB,BB]), %% Used to be one message, but now one per wrapped file. - ?line rec(2, {disk_log, node(), n, {wrap, 2}}), - ?line {194, 2} = {curb(n), cur_cnt(n)}, - ?line {16, 7} = {no_written_items(n), no_items(n)}, - ?line ok = disk_log:log_terms(n, [SB,SB,SB]), - ?line rec(1, {disk_log, node(), n, {wrap, 2}}), - ?line {80, 4} = {curb(n), cur_cnt(n)}, - ?line {20, 9} = {no_written_items(n), no_items(n)}, - ?line ok = disk_log:close(n), - ?line del(File, No), + rec(2, {disk_log, node(), n, {wrap, 2}}), + {194, 2} = {curb(n), cur_cnt(n)}, + {16, 7} = {no_written_items(n), no_items(n)}, + ok = disk_log:log_terms(n, [SB,SB,SB]), + rec(1, {disk_log, node(), n, {wrap, 2}}), + {80, 4} = {curb(n), cur_cnt(n)}, + {20, 9} = {no_written_items(n), no_items(n)}, + ok = disk_log:close(n), + del(File, No), %% Internal without header. - ?line {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, - {size, {100,No}}]), - ?line {8, 0} = {curb(n), cur_cnt(n)}, - ?line {0, 0} = {no_written_items(n), no_items(n)}, - ?line ok = disk_log:log(n, B), - ?line {76, 1} = {curb(n), cur_cnt(n)}, - ?line {1, 1} = {no_written_items(n), no_items(n)}, - ?line ok = disk_log:close(n), - ?line {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, - {notify, true}, {size, {100,No}}]), - ?line {76, 1} = {curb(n), cur_cnt(n)}, - ?line {0, 1} = {no_written_items(n), no_items(n)}, - ?line ok = disk_log:log(n, B), - ?line rec(1, {disk_log, node(), n, {wrap, 0}}), - ?line {76, 1} = {curb(n), cur_cnt(n)}, - ?line {1, 2} = {no_written_items(n), no_items(n)}, - ?line disk_log:inc_wrap_file(n), - ?line rec(1, {disk_log, node(), n, {wrap, 0}}), - ?line {8, 0} = {curb(n), cur_cnt(n)}, - ?line {1, 2} = {no_written_items(n), no_items(n)}, - ?line ok = disk_log:log_terms(n, [B,B,B]), + {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, + {size, {100,No}}]), + {8, 0} = {curb(n), cur_cnt(n)}, + {0, 0} = {no_written_items(n), no_items(n)}, + ok = disk_log:log(n, B), + {76, 1} = {curb(n), cur_cnt(n)}, + {1, 1} = {no_written_items(n), no_items(n)}, + ok = disk_log:close(n), + {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, + {notify, true}, {size, {100,No}}]), + {76, 1} = {curb(n), cur_cnt(n)}, + {0, 1} = {no_written_items(n), no_items(n)}, + ok = disk_log:log(n, B), + rec(1, {disk_log, node(), n, {wrap, 0}}), + {76, 1} = {curb(n), cur_cnt(n)}, + {1, 2} = {no_written_items(n), no_items(n)}, + disk_log:inc_wrap_file(n), + rec(1, {disk_log, node(), n, {wrap, 0}}), + {8, 0} = {curb(n), cur_cnt(n)}, + {1, 2} = {no_written_items(n), no_items(n)}, + ok = disk_log:log_terms(n, [B,B,B]), %% Used to be one message, but now one per wrapped file. - ?line rec(1, {disk_log, node(), n, {wrap, 0}}), - ?line rec(1, {disk_log, node(), n, {wrap, 1}}), - ?line {76, 1} = {curb(n), cur_cnt(n)}, - ?line {4, 4} = {no_written_items(n), no_items(n)}, - ?line ok = disk_log:log_terms(n, [B]), - ?line rec(1, {disk_log, node(), n, {wrap, 1}}), - ?line ok = disk_log:log_terms(n, [B]), - ?line rec(1, {disk_log, node(), n, {wrap, 1}}), - ?line {76, 1} = {curb(n), cur_cnt(n)}, - ?line {6, 4} = {no_written_items(n), no_items(n)}, - ?line ok = disk_log:log_terms(n, [BB,BB]), + rec(1, {disk_log, node(), n, {wrap, 0}}), + rec(1, {disk_log, node(), n, {wrap, 1}}), + {76, 1} = {curb(n), cur_cnt(n)}, + {4, 4} = {no_written_items(n), no_items(n)}, + ok = disk_log:log_terms(n, [B]), + rec(1, {disk_log, node(), n, {wrap, 1}}), + ok = disk_log:log_terms(n, [B]), + rec(1, {disk_log, node(), n, {wrap, 1}}), + {76, 1} = {curb(n), cur_cnt(n)}, + {6, 4} = {no_written_items(n), no_items(n)}, + ok = disk_log:log_terms(n, [BB,BB]), %% Used to be one message, but now one per wrapped file. - ?line rec(2, {disk_log, node(), n, {wrap, 1}}), - ?line {176, 1} = {curb(n), cur_cnt(n)}, - ?line {8, 4} = {no_written_items(n), no_items(n)}, - ?line ok = disk_log:log_terms(n, [SB,SB,SB]), - ?line rec(1, {disk_log, node(), n, {wrap, 1}}), - ?line {62, 3} = {curb(n), cur_cnt(n)}, - ?line {11, 6} = {no_written_items(n), no_items(n)}, - ?line ok = disk_log:close(n), - ?line del(File, No), + rec(2, {disk_log, node(), n, {wrap, 1}}), + {176, 1} = {curb(n), cur_cnt(n)}, + {8, 4} = {no_written_items(n), no_items(n)}, + ok = disk_log:log_terms(n, [SB,SB,SB]), + rec(1, {disk_log, node(), n, {wrap, 1}}), + {62, 3} = {curb(n), cur_cnt(n)}, + {11, 6} = {no_written_items(n), no_items(n)}, + ok = disk_log:close(n), + del(File, No), %% External with header. - ?line {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, - {format, external}, {head, "header"}, - {size, {100,No}}]), - ?line {6, 1} = {curb(n), cur_cnt(n)}, - ?line {1, 1} = {no_written_items(n), no_items(n)}, - ?line ok = disk_log:blog(n, B), - ?line {62, 2} = {curb(n), cur_cnt(n)}, - ?line {2, 2} = {no_written_items(n), no_items(n)}, - ?line ok = disk_log:close(n), - ?line {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, - {format, external}, {head, "header"}, - {notify, true}, {size, {100,No}}]), - ?line {62, 2} = {curb(n), cur_cnt(n)}, - ?line {0, 2} = {no_written_items(n), no_items(n)}, - ?line ok = disk_log:blog(n, B), - ?line rec(1, {disk_log, node(), n, {wrap, 0}}), - ?line {62, 2} = {curb(n), cur_cnt(n)}, - ?line {2, 4} = {no_written_items(n), no_items(n)}, - ?line disk_log:inc_wrap_file(n), - ?line rec(1, {disk_log, node(), n, {wrap, 0}}), - ?line {6, 1} = {curb(n), cur_cnt(n)}, - ?line {3, 4} = {no_written_items(n), no_items(n)}, - ?line ok = disk_log:blog_terms(n, [B,B,B]), + {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, + {format, external}, {head, "header"}, + {size, {100,No}}]), + {6, 1} = {curb(n), cur_cnt(n)}, + {1, 1} = {no_written_items(n), no_items(n)}, + ok = disk_log:blog(n, B), + {62, 2} = {curb(n), cur_cnt(n)}, + {2, 2} = {no_written_items(n), no_items(n)}, + ok = disk_log:close(n), + {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, + {format, external}, {head, "header"}, + {notify, true}, {size, {100,No}}]), + {62, 2} = {curb(n), cur_cnt(n)}, + {0, 2} = {no_written_items(n), no_items(n)}, + ok = disk_log:blog(n, B), + rec(1, {disk_log, node(), n, {wrap, 0}}), + {62, 2} = {curb(n), cur_cnt(n)}, + {2, 4} = {no_written_items(n), no_items(n)}, + disk_log:inc_wrap_file(n), + rec(1, {disk_log, node(), n, {wrap, 0}}), + {6, 1} = {curb(n), cur_cnt(n)}, + {3, 4} = {no_written_items(n), no_items(n)}, + ok = disk_log:blog_terms(n, [B,B,B]), %% Used to be one message, but now one per wrapped file. - ?line rec(1, {disk_log, node(), n, {wrap, 0}}), - ?line rec(1, {disk_log, node(), n, {wrap, 2}}), - ?line {62, 2} = {curb(n), cur_cnt(n)}, - ?line {8, 7} = {no_written_items(n), no_items(n)}, - ?line ok = disk_log:blog_terms(n, [B]), - ?line rec(1, {disk_log, node(), n, {wrap, 2}}), - ?line ok = disk_log:blog_terms(n, [B]), - ?line rec(1, {disk_log, node(), n, {wrap, 2}}), - ?line {62, 2} = {curb(n), cur_cnt(n)}, - ?line {12, 7} = {no_written_items(n), no_items(n)}, - ?line ok = disk_log:blog_terms(n, [BB,BB]), + rec(1, {disk_log, node(), n, {wrap, 0}}), + rec(1, {disk_log, node(), n, {wrap, 2}}), + {62, 2} = {curb(n), cur_cnt(n)}, + {8, 7} = {no_written_items(n), no_items(n)}, + ok = disk_log:blog_terms(n, [B]), + rec(1, {disk_log, node(), n, {wrap, 2}}), + ok = disk_log:blog_terms(n, [B]), + rec(1, {disk_log, node(), n, {wrap, 2}}), + {62, 2} = {curb(n), cur_cnt(n)}, + {12, 7} = {no_written_items(n), no_items(n)}, + ok = disk_log:blog_terms(n, [BB,BB]), %% Used to be one message, but now one per wrapped file. - ?line rec(2, {disk_log, node(), n, {wrap, 2}}), - ?line {162, 2} = {curb(n), cur_cnt(n)}, - ?line {16, 7} = {no_written_items(n), no_items(n)}, - ?line ok = disk_log:blog_terms(n, [SB,SB,SB]), - - ?line rec(1, {disk_log, node(), n, {wrap, 2}}), - ?line {24, 4} = {curb(n), cur_cnt(n)}, - ?line {20, 9} = {no_written_items(n), no_items(n)}, - ?line ok = disk_log:close(n), - ?line del(File, No), + rec(2, {disk_log, node(), n, {wrap, 2}}), + {162, 2} = {curb(n), cur_cnt(n)}, + {16, 7} = {no_written_items(n), no_items(n)}, + ok = disk_log:blog_terms(n, [SB,SB,SB]), + + rec(1, {disk_log, node(), n, {wrap, 2}}), + {24, 4} = {curb(n), cur_cnt(n)}, + {20, 9} = {no_written_items(n), no_items(n)}, + ok = disk_log:close(n), + del(File, No), %% External without header. - ?line {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, - {format, external}, {size, {100,No}}]), - ?line {0, 0} = {curb(n), cur_cnt(n)}, - ?line {0, 0} = {no_written_items(n), no_items(n)}, - ?line ok = disk_log:blog(n, B), - ?line {56, 1} = {curb(n), cur_cnt(n)}, - ?line {1, 1} = {no_written_items(n), no_items(n)}, - ?line ok = disk_log:close(n), - ?line {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, - {notify, true}, - {format, external}, {size, {100,No}}]), - ?line {56, 1} = {curb(n), cur_cnt(n)}, - ?line {0, 1} = {no_written_items(n), no_items(n)}, - ?line ok = disk_log:blog(n, B), - ?line rec(1, {disk_log, node(), n, {wrap, 0}}), - ?line {56, 1} = {curb(n), cur_cnt(n)}, - ?line {1, 2} = {no_written_items(n), no_items(n)}, - ?line disk_log:inc_wrap_file(n), - ?line rec(1, {disk_log, node(), n, {wrap, 0}}), - ?line {0, 0} = {curb(n), cur_cnt(n)}, - ?line {1, 2} = {no_written_items(n), no_items(n)}, - ?line ok = disk_log:blog_terms(n, [B,B,B]), + {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, + {format, external}, {size, {100,No}}]), + {0, 0} = {curb(n), cur_cnt(n)}, + {0, 0} = {no_written_items(n), no_items(n)}, + ok = disk_log:blog(n, B), + {56, 1} = {curb(n), cur_cnt(n)}, + {1, 1} = {no_written_items(n), no_items(n)}, + ok = disk_log:close(n), + {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, + {notify, true}, + {format, external}, {size, {100,No}}]), + {56, 1} = {curb(n), cur_cnt(n)}, + {0, 1} = {no_written_items(n), no_items(n)}, + ok = disk_log:blog(n, B), + rec(1, {disk_log, node(), n, {wrap, 0}}), + {56, 1} = {curb(n), cur_cnt(n)}, + {1, 2} = {no_written_items(n), no_items(n)}, + disk_log:inc_wrap_file(n), + rec(1, {disk_log, node(), n, {wrap, 0}}), + {0, 0} = {curb(n), cur_cnt(n)}, + {1, 2} = {no_written_items(n), no_items(n)}, + ok = disk_log:blog_terms(n, [B,B,B]), %% Used to be one message, but now one per wrapped file. - ?line rec(1, {disk_log, node(), n, {wrap, 0}}), - ?line rec(1, {disk_log, node(), n, {wrap, 1}}), - ?line {56, 1} = {curb(n), cur_cnt(n)}, - ?line {4, 4} = {no_written_items(n), no_items(n)}, - ?line ok = disk_log:blog_terms(n, [B]), - ?line rec(1, {disk_log, node(), n, {wrap, 1}}), - ?line ok = disk_log:blog_terms(n, [B]), - ?line rec(1, {disk_log, node(), n, {wrap, 1}}), - ?line {56, 1} = {curb(n), cur_cnt(n)}, - ?line {6, 4} = {no_written_items(n), no_items(n)}, - ?line ok = disk_log:blog_terms(n, [BB,BB]), + rec(1, {disk_log, node(), n, {wrap, 0}}), + rec(1, {disk_log, node(), n, {wrap, 1}}), + {56, 1} = {curb(n), cur_cnt(n)}, + {4, 4} = {no_written_items(n), no_items(n)}, + ok = disk_log:blog_terms(n, [B]), + rec(1, {disk_log, node(), n, {wrap, 1}}), + ok = disk_log:blog_terms(n, [B]), + rec(1, {disk_log, node(), n, {wrap, 1}}), + {56, 1} = {curb(n), cur_cnt(n)}, + {6, 4} = {no_written_items(n), no_items(n)}, + ok = disk_log:blog_terms(n, [BB,BB]), %% Used to be one message, but now one per wrapped file. - ?line rec(2, {disk_log, node(), n, {wrap, 1}}), - ?line {156, 1} = {curb(n), cur_cnt(n)}, - ?line {8, 4} = {no_written_items(n), no_items(n)}, - ?line ok = disk_log:blog_terms(n, [SB,SB,SB]), - ?line rec(1, {disk_log, node(), n, {wrap, 1}}), - ?line {18, 3} = {curb(n), cur_cnt(n)}, - ?line {11, 6} = {no_written_items(n), no_items(n)}, - ?line ok = disk_log:close(n), - ?line del(File, No), - - ?line Q = qlen(), + rec(2, {disk_log, node(), n, {wrap, 1}}), + {156, 1} = {curb(n), cur_cnt(n)}, + {8, 4} = {no_written_items(n), no_items(n)}, + ok = disk_log:blog_terms(n, [SB,SB,SB]), + rec(1, {disk_log, node(), n, {wrap, 1}}), + {18, 3} = {curb(n), cur_cnt(n)}, + {11, 6} = {no_written_items(n), no_items(n)}, + ok = disk_log:close(n), + del(File, No), + + Q = qlen(), ok. -change_size_before(suite) -> []; change_size_before(doc) -> ["Change size of a wrap log file before we have reached " "to the file index corresponding to the new size"]; @@ -3477,138 +3398,136 @@ change_size_before(Conf) when is_list(Conf) -> Dir = ?privdir(Conf), File = filename:join(Dir, "a.LOG"), del(File, 5), - ?line {ok, a} = disk_log:open([{name,a}, {file, File}, - {type, wrap}, {size, {100,5}}]), - ?line disk_log:log(a, Log_1_1), - ?line disk_log:log(a, Log_1_2), - ?line disk_log:log(a, Log_2_1), - ?line disk_log:log(a, Log_2_2), - ?line disk_log:change_size(a, {100, 3}), - ?line [Log_1_1, Log_1_2, - Log_2_1, Log_2_2] = get_all_terms(a), - ?line disk_log:log(a, Log_3_1), - ?line disk_log:log(a, Log_3_2), - ?line disk_log:log(a, Log_1_2_1), - ?line disk_log:log(a, Log_1_2_2), - ?line [Log_2_1, Log_2_2, - Log_3_1, Log_3_2, - Log_1_2_1, Log_1_2_2] = get_all_terms(a), - - ?line disk_log:close(a), - ?line {ok, a} = disk_log:open([{name,a}, {file, File}, {type, wrap}, - {size, {100,3}}]), - ?line [Log_2_1, Log_2_2, - Log_3_1, Log_3_2, - Log_1_2_1, Log_1_2_2] = get_all_terms(a), + {ok, a} = disk_log:open([{name,a}, {file, File}, + {type, wrap}, {size, {100,5}}]), + disk_log:log(a, Log_1_1), + disk_log:log(a, Log_1_2), + disk_log:log(a, Log_2_1), + disk_log:log(a, Log_2_2), + disk_log:change_size(a, {100, 3}), + [Log_1_1, Log_1_2, + Log_2_1, Log_2_2] = get_all_terms(a), + disk_log:log(a, Log_3_1), + disk_log:log(a, Log_3_2), + disk_log:log(a, Log_1_2_1), + disk_log:log(a, Log_1_2_2), + [Log_2_1, Log_2_2, + Log_3_1, Log_3_2, + Log_1_2_1, Log_1_2_2] = get_all_terms(a), + + disk_log:close(a), + {ok, a} = disk_log:open([{name,a}, {file, File}, {type, wrap}, + {size, {100,3}}]), + [Log_2_1, Log_2_2, + Log_3_1, Log_3_2, + Log_1_2_1, Log_1_2_2] = get_all_terms(a), disk_log:close(a), del(File, 5), - ?line {ok, a} = disk_log:open([{name,a}, {file, File}, {type, wrap}, - {size, {60,5}}, {format, external}]), - ?line disk_log:blog(a, Log_1_1), - ?line disk_log:blog(a, Log_1_2), - ?line disk_log:blog(a, Log_2_1), - ?line disk_log:blog(a, Log_2_2), - ?line disk_log:change_size(a, {60, 3}), - ?line ok = disk_log:sync(a), - ?line {ok, Fd1} = file:open(File ++ ".1", [read]), - ?line Log11_12 = Log_1_1 ++ Log_1_2, - ?line {ok,Log11_12} = file:read(Fd1, 200), - ?line ok = file:close(Fd1), - ?line {ok, Fd2} = file:open(File ++ ".2", [read]), -% ?t:format(0, "~p~n",[file:read(Fd2, 200)]), - ?line Log21_22 = Log_2_1 ++ Log_2_2, - ?line {ok,Log21_22} = file:read(Fd2, 200), - ?line ok = file:close(Fd2), - ?line disk_log:blog(a, Log_3_1), - ?line disk_log:blog(a, Log_3_2), - ?line disk_log:blog(a, Log_1_2_1), - ?line disk_log:blog(a, Log_1_2_2), - ?line ok = disk_log:sync(a), - ?line {ok, Fd2a} = file:open(File ++ ".2", [read]), - ?line {ok,Log21_22} = file:read(Fd2a, 200), - ?line ok = file:close(Fd2a), - ?line {ok, Fd3a} = file:open(File ++ ".3", [read]), - ?line Log31_32 = Log_3_1 ++ Log_3_2, - ?line {ok,Log31_32} = file:read(Fd3a, 200), - ?line ok = file:close(Fd3a), - ?line {ok, Fd1a} = file:open(File ++ ".1", [read]), - ?line Log121_122 = Log_1_2_1 ++ Log_1_2_2, - ?line {ok,Log121_122} = file:read(Fd1a, 200), - ?line ok = file:close(Fd1a), - - ?line disk_log:close(a), - ?line {ok, a} = disk_log:open([{name,a}, {file, File}, {type, wrap}, - {size, {60,3}}, {format, external}]), - ?line {ok, Fd2b} = file:open(File ++ ".2", [read]), - ?line {ok,Log21_22} = file:read(Fd2b, 200), - ?line ok = file:close(Fd2b), - ?line {ok, Fd3b} = file:open(File ++ ".3", [read]), - ?line {ok,Log31_32} = file:read(Fd3b, 200), - ?line ok = file:close(Fd3b), - ?line {ok, Fd1b} = file:open(File ++ ".1", [read]), - ?line {ok,Log121_122} = file:read(Fd1b, 200), - ?line ok = file:close(Fd1b), + {ok, a} = disk_log:open([{name,a}, {file, File}, {type, wrap}, + {size, {60,5}}, {format, external}]), + disk_log:blog(a, Log_1_1), + disk_log:blog(a, Log_1_2), + disk_log:blog(a, Log_2_1), + disk_log:blog(a, Log_2_2), + disk_log:change_size(a, {60, 3}), + ok = disk_log:sync(a), + {ok, Fd1} = file:open(File ++ ".1", [read]), + Log11_12 = Log_1_1 ++ Log_1_2, + {ok,Log11_12} = file:read(Fd1, 200), + ok = file:close(Fd1), + {ok, Fd2} = file:open(File ++ ".2", [read]), + Log21_22 = Log_2_1 ++ Log_2_2, + {ok,Log21_22} = file:read(Fd2, 200), + ok = file:close(Fd2), + disk_log:blog(a, Log_3_1), + disk_log:blog(a, Log_3_2), + disk_log:blog(a, Log_1_2_1), + disk_log:blog(a, Log_1_2_2), + ok = disk_log:sync(a), + {ok, Fd2a} = file:open(File ++ ".2", [read]), + {ok,Log21_22} = file:read(Fd2a, 200), + ok = file:close(Fd2a), + {ok, Fd3a} = file:open(File ++ ".3", [read]), + Log31_32 = Log_3_1 ++ Log_3_2, + {ok,Log31_32} = file:read(Fd3a, 200), + ok = file:close(Fd3a), + {ok, Fd1a} = file:open(File ++ ".1", [read]), + Log121_122 = Log_1_2_1 ++ Log_1_2_2, + {ok,Log121_122} = file:read(Fd1a, 200), + ok = file:close(Fd1a), + + disk_log:close(a), + {ok, a} = disk_log:open([{name,a}, {file, File}, {type, wrap}, + {size, {60,3}}, {format, external}]), + {ok, Fd2b} = file:open(File ++ ".2", [read]), + {ok,Log21_22} = file:read(Fd2b, 200), + ok = file:close(Fd2b), + {ok, Fd3b} = file:open(File ++ ".3", [read]), + {ok,Log31_32} = file:read(Fd3b, 200), + ok = file:close(Fd3b), + {ok, Fd1b} = file:open(File ++ ".1", [read]), + {ok,Log121_122} = file:read(Fd1b, 200), + ok = file:close(Fd1b), disk_log:close(a), del(File, 5), - ?line {ok, a} = disk_log:open([{name,a}, {file, File}, {type, wrap}, {size, {100,5}}]), - ?line disk_log:log(a, Log_1_1), - ?line disk_log:log(a, Log_1_2), - ?line disk_log:log(a, Log_2_1), - ?line disk_log:log(a, Log_2_2), - ?line disk_log:change_size(a, {60, 3}), - ?line [Log_1_1, Log_1_2, - Log_2_1, Log_2_2] = get_all_terms(a), - ?line disk_log:log(a, Log_3_1), - ?line disk_log:log(a, Log_1_2_1), - ?line [Log_2_1, Log_2_2, - Log_3_1, - Log_1_2_1] = get_all_terms(a), - - ?line disk_log:close(a), - ?line {ok, a} = disk_log:open([{name,a}, {file, File}, {type, wrap}, {size, {60,3}}]), - ?line [Log_2_1, Log_2_2, - Log_3_1, - Log_1_2_1] = get_all_terms(a), + {ok, a} = disk_log:open([{name,a}, {file, File}, {type, wrap}, {size, {100,5}}]), + disk_log:log(a, Log_1_1), + disk_log:log(a, Log_1_2), + disk_log:log(a, Log_2_1), + disk_log:log(a, Log_2_2), + disk_log:change_size(a, {60, 3}), + [Log_1_1, Log_1_2, + Log_2_1, Log_2_2] = get_all_terms(a), + disk_log:log(a, Log_3_1), + disk_log:log(a, Log_1_2_1), + [Log_2_1, Log_2_2, + Log_3_1, + Log_1_2_1] = get_all_terms(a), + + disk_log:close(a), + {ok, a} = disk_log:open([{name,a}, {file, File}, {type, wrap}, {size, {60,3}}]), + [Log_2_1, Log_2_2, + Log_3_1, + Log_1_2_1] = get_all_terms(a), disk_log:close(a), del(File, 5), - ?line {ok, a} = disk_log:open([{name,a}, {file, File}, {type, wrap}, {size, {60, 3}}]), - ?line disk_log:log(a, Log_1_1), - ?line disk_log:log(a, Log_2_1), - ?line disk_log:change_size(a, {100, 5}), - ?line [Log_1_1, - Log_2_1] = get_all_terms(a), - ?line disk_log:log(a, Log_2_2), - ?line disk_log:log(a, Log_3_1), - ?line disk_log:log(a, Log_3_2), - ?line disk_log:log(a, Log_4_1), - ?line disk_log:log(a, Log_4_2), - ?line disk_log:log(a, Log_5_1), - ?line disk_log:log(a, Log_5_2), - ?line disk_log:log(a, Log_1_2_1), - ?line [Log_2_1, Log_2_2, - Log_3_1, Log_3_2, - Log_4_1, Log_4_2, - Log_5_1, Log_5_2, - Log_1_2_1] = get_all_terms(a), - - ?line disk_log:close(a), - ?line {ok, a} = disk_log:open([{name,a}, {file, File}, {type, wrap}, {size, {100, 5}}]), - ?line [Log_2_1, Log_2_2, - Log_3_1, Log_3_2, - Log_4_1, Log_4_2, - Log_5_1, Log_5_2, - Log_1_2_1] = get_all_terms(a), + {ok, a} = disk_log:open([{name,a}, {file, File}, {type, wrap}, {size, {60, 3}}]), + disk_log:log(a, Log_1_1), + disk_log:log(a, Log_2_1), + disk_log:change_size(a, {100, 5}), + [Log_1_1, + Log_2_1] = get_all_terms(a), + disk_log:log(a, Log_2_2), + disk_log:log(a, Log_3_1), + disk_log:log(a, Log_3_2), + disk_log:log(a, Log_4_1), + disk_log:log(a, Log_4_2), + disk_log:log(a, Log_5_1), + disk_log:log(a, Log_5_2), + disk_log:log(a, Log_1_2_1), + [Log_2_1, Log_2_2, + Log_3_1, Log_3_2, + Log_4_1, Log_4_2, + Log_5_1, Log_5_2, + Log_1_2_1] = get_all_terms(a), + + disk_log:close(a), + {ok, a} = disk_log:open([{name,a}, {file, File}, {type, wrap}, {size, {100, 5}}]), + [Log_2_1, Log_2_2, + Log_3_1, Log_3_2, + Log_4_1, Log_4_2, + Log_5_1, Log_5_2, + Log_1_2_1] = get_all_terms(a), disk_log:close(a), del(File, 5). -change_size_during(suite) -> []; -change_size_during(doc) -> ["Change size of a wrap log file while logging " - "to a file index between the old and the new size"]; +%% Change size of a wrap log file while logging to a file index +%% between the old and the new size. change_size_during(Conf) when is_list(Conf) -> Log_1_1 = "first log first message", @@ -3632,114 +3551,111 @@ change_size_during(Conf) when is_list(Conf) -> Dir = ?privdir(Conf), File = filename:join(Dir, "a.LOG"), - ?line {ok, a} = disk_log:open([{name,a}, {file, File}, {type, wrap}, {size, {100,5}}]), - ?line disk_log:log(a, Log_1_1), - ?line disk_log:log(a, Log_1_2), - ?line disk_log:log(a, Log_2_1), - ?line disk_log:log(a, Log_2_2), - ?line disk_log:log(a, Log_3_1), - ?line disk_log:log(a, Log_3_2), - ?line disk_log:log(a, Log_4_1), - ?line disk_log:log(a, Log_4_2), - ?line disk_log:log(a, Log_5_1), - ?line disk_log:log(a, Log_5_2), - ?line disk_log:log(a, Log_1_1), - ?line disk_log:log(a, Log_1_2), - ?line disk_log:log(a, Log_2_1), - ?line disk_log:log(a, Log_2_2), - ?line disk_log:log(a, Log_3_1), - ?line disk_log:log(a, Log_3_2), - ?line disk_log:log(a, Log_4_1), - ?line disk_log:log(a, Log_4_2), - ?line disk_log:change_size(a, {100, 3}), - ?line [Log_5_1, Log_5_2, - Log_1_1, Log_1_2, - Log_2_1, Log_2_2, - Log_3_1, Log_3_2, - Log_4_1, Log_4_2] = get_all_terms(a), - ?line disk_log:log(a, Log_1_2_1), - ?line disk_log:log(a, Log_1_2_2), - ?line [Log_2_1, Log_2_2, - Log_3_1, Log_3_2, - Log_4_1, Log_4_2, - Log_1_2_1, Log_1_2_2] = get_all_terms(a), - - ?line disk_log:close(a), - ?line {ok, a} = disk_log:open([{name,a}, {file, File}, {type, wrap}, {size, {100,3}}]), - ?line [Log_2_1, Log_2_2, - Log_3_1, Log_3_2, - Log_4_1, Log_4_2, - Log_1_2_1, Log_1_2_2] = get_all_terms(a), - ?line disk_log:log(a, Log_2_2_1), - ?line disk_log:log(a, Log_2_2_2), - ?line disk_log:log(a, Log_3_2_1), - ?line disk_log:log(a, Log_3_2_2), - ?line disk_log:log(a, Log_1_3_1), - ?line disk_log:log(a, Log_1_3_2), - ?line [Log_2_2_1, Log_2_2_2, - Log_3_2_1, Log_3_2_2, - Log_1_3_1, Log_1_3_2] = get_all_terms(a), + {ok, a} = disk_log:open([{name,a}, {file, File}, {type, wrap}, {size, {100,5}}]), + disk_log:log(a, Log_1_1), + disk_log:log(a, Log_1_2), + disk_log:log(a, Log_2_1), + disk_log:log(a, Log_2_2), + disk_log:log(a, Log_3_1), + disk_log:log(a, Log_3_2), + disk_log:log(a, Log_4_1), + disk_log:log(a, Log_4_2), + disk_log:log(a, Log_5_1), + disk_log:log(a, Log_5_2), + disk_log:log(a, Log_1_1), + disk_log:log(a, Log_1_2), + disk_log:log(a, Log_2_1), + disk_log:log(a, Log_2_2), + disk_log:log(a, Log_3_1), + disk_log:log(a, Log_3_2), + disk_log:log(a, Log_4_1), + disk_log:log(a, Log_4_2), + disk_log:change_size(a, {100, 3}), + [Log_5_1, Log_5_2, + Log_1_1, Log_1_2, + Log_2_1, Log_2_2, + Log_3_1, Log_3_2, + Log_4_1, Log_4_2] = get_all_terms(a), + disk_log:log(a, Log_1_2_1), + disk_log:log(a, Log_1_2_2), + [Log_2_1, Log_2_2, + Log_3_1, Log_3_2, + Log_4_1, Log_4_2, + Log_1_2_1, Log_1_2_2] = get_all_terms(a), + + disk_log:close(a), + {ok, a} = disk_log:open([{name,a}, {file, File}, {type, wrap}, {size, {100,3}}]), + [Log_2_1, Log_2_2, + Log_3_1, Log_3_2, + Log_4_1, Log_4_2, + Log_1_2_1, Log_1_2_2] = get_all_terms(a), + disk_log:log(a, Log_2_2_1), + disk_log:log(a, Log_2_2_2), + disk_log:log(a, Log_3_2_1), + disk_log:log(a, Log_3_2_2), + disk_log:log(a, Log_1_3_1), + disk_log:log(a, Log_1_3_2), + [Log_2_2_1, Log_2_2_2, + Log_3_2_1, Log_3_2_2, + Log_1_3_1, Log_1_3_2] = get_all_terms(a), disk_log:close(a), - ?line {ok, a} = disk_log:open([{name,a}, {file, File}, {type, wrap}, {size, {100,3}}]), - ?line [Log_2_2_1, Log_2_2_2, - Log_3_2_1, Log_3_2_2, - Log_1_3_1, Log_1_3_2] = get_all_terms(a), + {ok, a} = disk_log:open([{name,a}, {file, File}, {type, wrap}, {size, {100,3}}]), + [Log_2_2_1, Log_2_2_2, + Log_3_2_1, Log_3_2_2, + Log_1_3_1, Log_1_3_2] = get_all_terms(a), disk_log:close(a), del(File, 5), - ?line {ok, a} = disk_log:open([{name,a}, {file, File}, {type, wrap}, {size, {100,5}}]), - ?line disk_log:log(a, Log_1_1), - ?line disk_log:log(a, Log_1_2), - ?line disk_log:log(a, Log_2_1), - ?line disk_log:log(a, Log_2_2), - ?line disk_log:log(a, Log_3_1), - ?line disk_log:log(a, Log_3_2), - ?line disk_log:log(a, Log_4_1), - ?line disk_log:log(a, Log_4_2), - ?line disk_log:log(a, Log_5_1), - ?line disk_log:log(a, Log_5_2), - ?line disk_log:log(a, Log_1_1), - ?line disk_log:log(a, Log_1_2), - ?line disk_log:log(a, Log_2_1), - ?line disk_log:log(a, Log_2_2), - ?line disk_log:log(a, Log_3_1), - ?line disk_log:log(a, Log_3_2), - ?line disk_log:log(a, Log_4_1), - ?line disk_log:log(a, Log_4_2), - ?line disk_log:log(a, Log_5_1), - ?line disk_log:log(a, Log_5_2), - ?line disk_log:change_size(a, {100, 3}), - ?line [Log_1_1, Log_1_2, - Log_2_1, Log_2_2, - Log_3_1, Log_3_2, - Log_4_1, Log_4_2, - Log_5_1, Log_5_2] = get_all_terms(a), - ?line disk_log:log(a, Log_1_2_1), - ?line disk_log:log(a, Log_1_2_2), - ?line disk_log:log(a, Log_2_2_1), - ?line disk_log:log(a, Log_2_2_2), - ?line disk_log:log(a, Log_3_2_1), - ?line disk_log:log(a, Log_3_2_2), - ?line disk_log:log(a, Log_1_3_1), - ?line disk_log:log(a, Log_1_3_2), - ?line [Log_2_2_1, Log_2_2_2, - Log_3_2_1, Log_3_2_2, - Log_1_3_1, Log_1_3_2] = get_all_terms(a), - - ?line disk_log:close(a), - ?line {ok, a} = disk_log:open([{name,a}, {file, File}, {type, wrap}, {size, {100,3}}]), - ?line [Log_2_2_1, Log_2_2_2, - Log_3_2_1, Log_3_2_2, - Log_1_3_1, Log_1_3_2] = get_all_terms(a), + {ok, a} = disk_log:open([{name,a}, {file, File}, {type, wrap}, {size, {100,5}}]), + disk_log:log(a, Log_1_1), + disk_log:log(a, Log_1_2), + disk_log:log(a, Log_2_1), + disk_log:log(a, Log_2_2), + disk_log:log(a, Log_3_1), + disk_log:log(a, Log_3_2), + disk_log:log(a, Log_4_1), + disk_log:log(a, Log_4_2), + disk_log:log(a, Log_5_1), + disk_log:log(a, Log_5_2), + disk_log:log(a, Log_1_1), + disk_log:log(a, Log_1_2), + disk_log:log(a, Log_2_1), + disk_log:log(a, Log_2_2), + disk_log:log(a, Log_3_1), + disk_log:log(a, Log_3_2), + disk_log:log(a, Log_4_1), + disk_log:log(a, Log_4_2), + disk_log:log(a, Log_5_1), + disk_log:log(a, Log_5_2), + disk_log:change_size(a, {100, 3}), + [Log_1_1, Log_1_2, + Log_2_1, Log_2_2, + Log_3_1, Log_3_2, + Log_4_1, Log_4_2, + Log_5_1, Log_5_2] = get_all_terms(a), + disk_log:log(a, Log_1_2_1), + disk_log:log(a, Log_1_2_2), + disk_log:log(a, Log_2_2_1), + disk_log:log(a, Log_2_2_2), + disk_log:log(a, Log_3_2_1), + disk_log:log(a, Log_3_2_2), + disk_log:log(a, Log_1_3_1), + disk_log:log(a, Log_1_3_2), + [Log_2_2_1, Log_2_2_2, + Log_3_2_1, Log_3_2_2, + Log_1_3_1, Log_1_3_2] = get_all_terms(a), + + disk_log:close(a), + {ok, a} = disk_log:open([{name,a}, {file, File}, {type, wrap}, {size, {100,3}}]), + [Log_2_2_1, Log_2_2_2, + Log_3_2_1, Log_3_2_2, + Log_1_3_1, Log_1_3_2] = get_all_terms(a), disk_log:close(a), del(File, 5). -change_size_after(suite) -> []; -change_size_after(doc) -> - ["Change size of a wrap log file before we have reached " - "(on the second round) " - "to the file index corresponding to the new size"]; +%% Change size of a wrap log file before we have reached (on the +%% second round) to the file index corresponding to the new size. change_size_after(Conf) when is_list(Conf) -> Log_1_1 = "first log first message", @@ -3757,172 +3673,169 @@ change_size_after(Conf) when is_list(Conf) -> Dir = ?privdir(Conf), File = filename:join(Dir, "a.LOG"), - ?line {ok, a} = disk_log:open([{name,a}, {file, File}, {type, wrap}, - {size, {100,5}}]), - ?line disk_log:log(a, Log_1_1), - ?line disk_log:log(a, Log_1_2), - ?line disk_log:log(a, Log_2_1), - ?line disk_log:log(a, Log_2_2), - ?line disk_log:log(a, Log_3_1), - ?line disk_log:log(a, Log_3_2), - ?line disk_log:log(a, Log_4_1), - ?line disk_log:log(a, Log_4_2), - ?line disk_log:log(a, Log_5_1), - ?line disk_log:log(a, Log_5_2), - ?line disk_log:log(a, Log_1_1), - ?line disk_log:log(a, Log_1_2), - ?line disk_log:log(a, Log_2_1), - ?line disk_log:log(a, Log_2_2), - ?line disk_log:change_size(a, {100, 3}), - ?line [Log_3_1,Log_3_2, - Log_4_1, Log_4_2, - Log_5_1, Log_5_2, - Log_1_1, Log_1_2, - Log_2_1, Log_2_2] = get_all_terms(a), - ?line disk_log:log(a, Log_3_1), - ?line disk_log:log(a, Log_3_2), - ?line disk_log:log(a, Log_1_2_1), - ?line disk_log:log(a, Log_1_2_2), - ?line [Log_2_1, Log_2_2, - Log_3_1, Log_3_2, - Log_1_2_1, Log_1_2_2] = get_all_terms(a), - - ?line disk_log:close(a), - ?line {ok, a} = disk_log:open([{name,a}, {file, File}, {type, wrap}, - {size, {100,3}}]), - ?line [Log_2_1, Log_2_2, - Log_3_1, Log_3_2, - Log_1_2_1, Log_1_2_2] = get_all_terms(a), + {ok, a} = disk_log:open([{name,a}, {file, File}, {type, wrap}, + {size, {100,5}}]), + disk_log:log(a, Log_1_1), + disk_log:log(a, Log_1_2), + disk_log:log(a, Log_2_1), + disk_log:log(a, Log_2_2), + disk_log:log(a, Log_3_1), + disk_log:log(a, Log_3_2), + disk_log:log(a, Log_4_1), + disk_log:log(a, Log_4_2), + disk_log:log(a, Log_5_1), + disk_log:log(a, Log_5_2), + disk_log:log(a, Log_1_1), + disk_log:log(a, Log_1_2), + disk_log:log(a, Log_2_1), + disk_log:log(a, Log_2_2), + disk_log:change_size(a, {100, 3}), + [Log_3_1,Log_3_2, + Log_4_1, Log_4_2, + Log_5_1, Log_5_2, + Log_1_1, Log_1_2, + Log_2_1, Log_2_2] = get_all_terms(a), + disk_log:log(a, Log_3_1), + disk_log:log(a, Log_3_2), + disk_log:log(a, Log_1_2_1), + disk_log:log(a, Log_1_2_2), + [Log_2_1, Log_2_2, + Log_3_1, Log_3_2, + Log_1_2_1, Log_1_2_2] = get_all_terms(a), + + disk_log:close(a), + {ok, a} = disk_log:open([{name,a}, {file, File}, {type, wrap}, + {size, {100,3}}]), + [Log_2_1, Log_2_2, + Log_3_1, Log_3_2, + Log_1_2_1, Log_1_2_2] = get_all_terms(a), disk_log:close(a), del(File, 5), - ?line {ok, a} = disk_log:open([{name,a}, {file, File}, {type, wrap}, - {size, {100,5}}]), - ?line disk_log:log(a, Log_1_1), - ?line disk_log:log(a, Log_1_2), - ?line disk_log:log(a, Log_2_1), - ?line disk_log:log(a, Log_2_2), - ?line disk_log:log(a, Log_3_1), - ?line disk_log:log(a, Log_3_2), - ?line disk_log:log(a, Log_4_1), - ?line disk_log:log(a, Log_4_2), - ?line disk_log:log(a, Log_5_1), - ?line disk_log:log(a, Log_5_2), - ?line disk_log:log(a, Log_1_1), - ?line disk_log:log(a, Log_1_2), - ?line disk_log:log(a, Log_2_1), - ?line disk_log:log(a, Log_2_2), - ?line disk_log:change_size(a, {60, 3}), - ?line [Log_3_1,Log_3_2, - Log_4_1, Log_4_2, - Log_5_1, Log_5_2, - Log_1_1, Log_1_2, - Log_2_1, Log_2_2] = get_all_terms(a), - ?line disk_log:log(a, Log_3_1), - ?line disk_log:log(a, Log_1_2_1), - ?line [Log_2_1, Log_2_2, - Log_3_1, - Log_1_2_1] = get_all_terms(a), - - ?line disk_log:close(a), - ?line {ok, a} = disk_log:open([{name,a}, {file, File}, {type, wrap}, - {size, {60,3}}]), - ?line [Log_2_1, Log_2_2, - Log_3_1, - Log_1_2_1] = get_all_terms(a), + {ok, a} = disk_log:open([{name,a}, {file, File}, {type, wrap}, + {size, {100,5}}]), + disk_log:log(a, Log_1_1), + disk_log:log(a, Log_1_2), + disk_log:log(a, Log_2_1), + disk_log:log(a, Log_2_2), + disk_log:log(a, Log_3_1), + disk_log:log(a, Log_3_2), + disk_log:log(a, Log_4_1), + disk_log:log(a, Log_4_2), + disk_log:log(a, Log_5_1), + disk_log:log(a, Log_5_2), + disk_log:log(a, Log_1_1), + disk_log:log(a, Log_1_2), + disk_log:log(a, Log_2_1), + disk_log:log(a, Log_2_2), + disk_log:change_size(a, {60, 3}), + [Log_3_1,Log_3_2, + Log_4_1, Log_4_2, + Log_5_1, Log_5_2, + Log_1_1, Log_1_2, + Log_2_1, Log_2_2] = get_all_terms(a), + disk_log:log(a, Log_3_1), + disk_log:log(a, Log_1_2_1), + [Log_2_1, Log_2_2, + Log_3_1, + Log_1_2_1] = get_all_terms(a), + + disk_log:close(a), + {ok, a} = disk_log:open([{name,a}, {file, File}, {type, wrap}, + {size, {60,3}}]), + [Log_2_1, Log_2_2, + Log_3_1, + Log_1_2_1] = get_all_terms(a), disk_log:close(a), del(File, 5). -default_size(suite) -> []; -default_size(doc) -> ["Open an existing wrap log without size option "]; +%% Open an existing wrap log without size option . default_size(Conf) when is_list(Conf) -> - ?line Dir = ?privdir(Conf), - ?line File = filename:join(Dir, "a.LOG"), - ?line {error, {badarg, size}} = disk_log:open([{name,a}, {file, File}, + Dir = ?privdir(Conf), + File = filename:join(Dir, "a.LOG"), + {error, {badarg, size}} = disk_log:open([{name,a}, {file, File}, {type, wrap}]), - ?line {ok, a} = disk_log:open([{name,a}, {file, File}, {type, wrap}, + {ok, a} = disk_log:open([{name,a}, {file, File}, {type, wrap}, {size, {100,5}}]), - ?line disk_log:close(a), + disk_log:close(a), - ?line {ok, a} = disk_log:open([{name,a}, {file, File}, {type, wrap}]), - ?line {100, 5} = disk_log_1:read_size_file(File), - ?line ok = disk_log:close(a), - ?line del(File, 5). + {ok, a} = disk_log:open([{name,a}, {file, File}, {type, wrap}]), + {100, 5} = disk_log_1:read_size_file(File), + ok = disk_log:close(a), + del(File, 5). -change_size2(suite) -> []; -change_size2(doc) -> ["Testing change_size/2 a bit more..."]; +%% Testing change_size/2 a bit more... change_size2(Conf) when is_list(Conf) -> Dir = ?privdir(Conf), - ?line File = filename:join(Dir, "n.LOG"), - ?line No = 4, - ?line del(File, No), % cleanup + File = filename:join(Dir, "n.LOG"), + No = 4, + del(File, No), % cleanup %% External halt. - ?line {ok, n} = disk_log:open([{name, n}, {file, File}, {size, 100000}, + {ok, n} = disk_log:open([{name, n}, {file, File}, {size, 100000}, {format, external}, {type, halt}]), - ?line B = mk_bytes(60), % 56 actually... - ?line ok = disk_log:blog_terms(n, [B,list_to_binary(B),B]), - ?line Error1 = {error, {new_size_too_small,n,168}} = + B = mk_bytes(60), % 56 actually... + ok = disk_log:blog_terms(n, [B,list_to_binary(B),B]), + Error1 = {error, {new_size_too_small,n,168}} = disk_log:change_size(n, 167), - ?line "The current size" ++ _ = format_error(Error1), - ?line ok = disk_log:change_size(n, infinity), - ?line ok = disk_log:change_size(n, 168), - ?line ok = disk_log:close(n), - ?line file:delete(File), % cleanup + "The current size" ++ _ = format_error(Error1), + ok = disk_log:change_size(n, infinity), + ok = disk_log:change_size(n, 168), + ok = disk_log:close(n), + file:delete(File), % cleanup %% External wrap. - ?line {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, + {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, {size, {100,No}}, {notify, true}, {format, external}]), - ?line BB = mk_bytes(160), - ?line ok = disk_log:blog_terms(n, [BB, BB, BB, BB]), % create all files + BB = mk_bytes(160), + ok = disk_log:blog_terms(n, [BB, BB, BB, BB]), % create all files %% Used to be one message, but now one per wrapped file. - ?line rec(3, {disk_log, node(), n, {wrap, 0}}), - ?line ok = disk_log:blog_terms(n, [BB, BB]), + rec(3, {disk_log, node(), n, {wrap, 0}}), + ok = disk_log:blog_terms(n, [BB, BB]), %% Used to be one message, but now one per wrapped file. - ?line rec(2, {disk_log, node(), n, {wrap, 1}}), - ?line ok = disk_log:change_size(n, {100, 2}), - ?line ok = disk_log:change_size(n, {100, 2}), - ?line {100, 2} = sz(n), - ?line ok = disk_log:balog_terms(n, [BB, BB]), - ?line ok = disk_log:balog_terms(n, [BB]), - ?line ok = disk_log:blog_terms(n, [BB]), + rec(2, {disk_log, node(), n, {wrap, 1}}), + ok = disk_log:change_size(n, {100, 2}), + ok = disk_log:change_size(n, {100, 2}), + {100, 2} = sz(n), + ok = disk_log:balog_terms(n, [BB, BB]), + ok = disk_log:balog_terms(n, [BB]), + ok = disk_log:blog_terms(n, [BB]), %% Used to be one message, but now one per wrapped file. - ?line rec(4, {disk_log, node(), n, {wrap, 1}}), - ?line ok = disk_log:change_size(n, {100, 4}), - ?line ok = disk_log:close(n), - ?line del(File, No), + rec(4, {disk_log, node(), n, {wrap, 1}}), + ok = disk_log:change_size(n, {100, 4}), + ok = disk_log:close(n), + del(File, No), %% Internal wrap. - ?line {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, + {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, {size, {100,No}}, {notify, true}, {format, internal}]), - ?line ok = disk_log:blog_terms(n, [BB, BB, BB, BB]), % create all files + ok = disk_log:blog_terms(n, [BB, BB, BB, BB]), % create all files %% Used to be one message, but now one per wrapped file. - ?line rec(3, {disk_log, node(), n, {wrap, 0}}), - ?line ok = disk_log:blog_terms(n, [BB, BB]), + rec(3, {disk_log, node(), n, {wrap, 0}}), + ok = disk_log:blog_terms(n, [BB, BB]), %% Used to be one message, but now one per wrapped file. - ?line rec(2, {disk_log, node(), n, {wrap, 1}}), - ?line ok = disk_log:change_size(n, {100, 2}), - ?line {100, 2} = sz(n), - ?line ok = disk_log:blog_terms(n, [BB, BB, BB, BB]), + rec(2, {disk_log, node(), n, {wrap, 1}}), + ok = disk_log:change_size(n, {100, 2}), + {100, 2} = sz(n), + ok = disk_log:blog_terms(n, [BB, BB, BB, BB]), %% Used to be one message, but now one per wrapped file. - ?line rec(4, {disk_log, node(), n, {wrap, 1}}), - ?line ok = disk_log:close(n), - ?line del(File, No). + rec(4, {disk_log, node(), n, {wrap, 1}}), + ok = disk_log:close(n), + del(File, No). -change_size_truncate(suite) -> []; -change_size_truncate(doc) -> ["OTP-3484: truncating index file"]; +%% OTP-3484: truncating index file. change_size_truncate(Conf) when is_list(Conf) -> Dir = ?privdir(Conf), - ?line File = filename:join(Dir, "bert.LOG"), - ?line No = 3, - ?line B = mk_bytes(60), + File = filename:join(Dir, "bert.LOG"), + No = 3, + B = mk_bytes(60), %% The problem here is truncation of the index file. One cannot easily %% check that the index file is correctly updated, but print_index_file() @@ -3932,541 +3845,527 @@ change_size_truncate(Conf) when is_list(Conf) -> %% Change the size immediately after creating the log, while there %% are no log files. This used to write stuff a negative offset %% from the beginning of the file. - ?line del(File, No+1), - ?line {ok, bert} = disk_log:open([{name,bert}, {type,wrap}, {file, File}, + del(File, No+1), + {ok, bert} = disk_log:open([{name,bert}, {type,wrap}, {file, File}, {notify, true}, {size,{1000,255}}]), - ?line ok = disk_log:change_size(bert,{100,No}), - ?line ok = disk_log:blog(bert, B), - ?line ok = disk_log:blog(bert, B), - ?line rec(1, {disk_log, node(), bert, {wrap, 0}}), - ?line ok = disk_log:blog(bert, B), - ?line rec(1, {disk_log, node(), bert, {wrap, 0}}), - ?line 3 = curf(bert), - ?line ok = disk_log:blog(bert, B), - ?line rec(1, {disk_log, node(), bert, {wrap, 1}}), - ?line 1 = curf(bert), - ?line ok = disk_log:blog(bert, B), - ?line rec(1, {disk_log, node(), bert, {wrap, 1}}), - ?line ok = disk_log:blog(bert, B), - ?line rec(1, {disk_log, node(), bert, {wrap, 1}}), - - ?line ok = disk_log:blog(bert, B), - ?line rec(1, {disk_log, node(), bert, {wrap, 1}}), - ?line ok = disk_log:blog(bert, B), - ?line rec(1, {disk_log, node(), bert, {wrap, 1}}), - ?line ok = disk_log:blog(bert, B), - ?line rec(1, {disk_log, node(), bert, {wrap, 1}}), - - % Three items expected. - % disk_log_1:print_index_file("bert.LOG.idx"), - ?line 3 = curf(bert), - ?line ok = disk_log:change_size(bert,{100,1}), - ?line ok = disk_log:blog(bert, B), - ?line rec(1, {disk_log, node(), bert, {wrap, 1}}), - % Three items expected. - % disk_log_1:print_index_file("bert.LOG.idx"), - ?line ok = disk_log:blog(bert, B), - ?line rec(1, {disk_log, node(), bert, {wrap, 1}}), - ?line ok = disk_log:blog(bert, B), - ?line rec(1, {disk_log, node(), bert, {wrap, 1}}), - % One item expected. - % disk_log_1:print_index_file("bert.LOG.idx"), - - ?line ok = disk_log:blog(bert, B), - ?line rec(1, {disk_log, node(), bert, {wrap, 1}}), - ?line ok = disk_log:close(bert), - ?line del(File, No), + ok = disk_log:change_size(bert,{100,No}), + ok = disk_log:blog(bert, B), + ok = disk_log:blog(bert, B), + rec(1, {disk_log, node(), bert, {wrap, 0}}), + ok = disk_log:blog(bert, B), + rec(1, {disk_log, node(), bert, {wrap, 0}}), + 3 = curf(bert), + ok = disk_log:blog(bert, B), + rec(1, {disk_log, node(), bert, {wrap, 1}}), + 1 = curf(bert), + ok = disk_log:blog(bert, B), + rec(1, {disk_log, node(), bert, {wrap, 1}}), + ok = disk_log:blog(bert, B), + rec(1, {disk_log, node(), bert, {wrap, 1}}), + + ok = disk_log:blog(bert, B), + rec(1, {disk_log, node(), bert, {wrap, 1}}), + ok = disk_log:blog(bert, B), + rec(1, {disk_log, node(), bert, {wrap, 1}}), + ok = disk_log:blog(bert, B), + rec(1, {disk_log, node(), bert, {wrap, 1}}), + + %% Three items expected. + %% disk_log_1:print_index_file("bert.LOG.idx"), + 3 = curf(bert), + ok = disk_log:change_size(bert,{100,1}), + ok = disk_log:blog(bert, B), + rec(1, {disk_log, node(), bert, {wrap, 1}}), + %% Three items expected. + %% disk_log_1:print_index_file("bert.LOG.idx"), + ok = disk_log:blog(bert, B), + rec(1, {disk_log, node(), bert, {wrap, 1}}), + ok = disk_log:blog(bert, B), + rec(1, {disk_log, node(), bert, {wrap, 1}}), + %% One item expected. + %% disk_log_1:print_index_file("bert.LOG.idx"), + + ok = disk_log:blog(bert, B), + rec(1, {disk_log, node(), bert, {wrap, 1}}), + ok = disk_log:close(bert), + del(File, No), %% Part 2. %% Change the size twice, the second time while the the effects of %% the first changed have not yet been handled. Finally close before %% the index file has been truncated. - ?line del(File, No), - ?line {ok, bert} = disk_log:open([{name,bert}, {type,wrap}, {file, File}, + del(File, No), + {ok, bert} = disk_log:open([{name,bert}, {type,wrap}, {file, File}, {notify, true}, {size,{100,No}}]), - ?line ok = disk_log:blog(bert, B), - ?line ok = disk_log:blog(bert, B), - ?line rec(1, {disk_log, node(), bert, {wrap, 0}}), - ?line ok = disk_log:blog(bert, B), - ?line rec(1, {disk_log, node(), bert, {wrap, 0}}), + ok = disk_log:blog(bert, B), + ok = disk_log:blog(bert, B), + rec(1, {disk_log, node(), bert, {wrap, 0}}), + ok = disk_log:blog(bert, B), + rec(1, {disk_log, node(), bert, {wrap, 0}}), - ?line 3 = curf(bert), - ?line ok = disk_log:change_size(bert,{100,No-1}), + 3 = curf(bert), + ok = disk_log:change_size(bert,{100,No-1}), - ?line ok = disk_log:blog(bert, B), - ?line rec(1, {disk_log, node(), bert, {wrap, 1}}), + ok = disk_log:blog(bert, B), + rec(1, {disk_log, node(), bert, {wrap, 1}}), - ?line 1 = curf(bert), - ?line ok = disk_log:change_size(bert,{100,No+1}), + 1 = curf(bert), + ok = disk_log:change_size(bert,{100,No+1}), - % Three items expected. - % disk_log_1:print_index_file("bert.LOG.idx"), + %% Three items expected. + %% disk_log_1:print_index_file("bert.LOG.idx"), - ?line ok = disk_log:blog(bert, B), - ?line rec(1, {disk_log, node(), bert, {wrap, 1}}), + ok = disk_log:blog(bert, B), + rec(1, {disk_log, node(), bert, {wrap, 1}}), - % Three items expected. - % disk_log_1:print_index_file("bert.LOG.idx"), + %% Three items expected. + %% disk_log_1:print_index_file("bert.LOG.idx"), - ?line 2 = curf(bert), - ?line ok = disk_log:change_size(bert,{100,1}), + 2 = curf(bert), + ok = disk_log:change_size(bert,{100,1}), - % Three items expected. - % disk_log_1:print_index_file("bert.LOG.idx"), + %% Three items expected. + %% disk_log_1:print_index_file("bert.LOG.idx"), - ?line ok = disk_log:close(bert), + ok = disk_log:close(bert), - % State: .siz is 1, current file is 2, index file size is 3... + %% State: .siz is 1, current file is 2, index file size is 3... - ?line {ok, bert} = disk_log:open([{name,bert}, {file, File}, + {ok, bert} = disk_log:open([{name,bert}, {file, File}, {type,wrap}, {notify, true}]), - % Three items expected. - % disk_log_1:print_index_file("bert.LOG.idx"), + %% Three items expected. + %% disk_log_1:print_index_file("bert.LOG.idx"), - ?line 2 = curf(bert), - ?line ok = disk_log:blog(bert, B), - ?line rec(1, {disk_log, node(), bert, {wrap, 1}}), - ?line ok = disk_log:close(bert), + 2 = curf(bert), + ok = disk_log:blog(bert, B), + rec(1, {disk_log, node(), bert, {wrap, 1}}), + ok = disk_log:close(bert), - ?line {ok, bert} = disk_log:open([{name,bert}, {file, File}, + {ok, bert} = disk_log:open([{name,bert}, {file, File}, {type,wrap}, {notify, true}]), - % Two items expected. - % disk_log_1:print_index_file("bert.LOG.idx"), + %% Two items expected. + %% disk_log_1:print_index_file("bert.LOG.idx"), - ?line 1 = curf(bert), - ?line ok = disk_log:blog(bert, B), + 1 = curf(bert), + ok = disk_log:blog(bert, B), %% Expect {wrap 0}. Nothing lost now, last wrap notification %% reported one lost item. - ?line rec(1, {disk_log, node(), bert, {wrap, 0}}), + rec(1, {disk_log, node(), bert, {wrap, 0}}), - % One item expected. - % disk_log_1:print_index_file("bert.LOG.idx"), - ?line ok = disk_log:close(bert), + %% One item expected. + %% disk_log_1:print_index_file("bert.LOG.idx"), + ok = disk_log:close(bert), - ?line del(File, No), + del(File, No), ok. -change_attribute(suite) -> []; -change_attribute(doc) -> - ["Change notify and head"]; +%% Change notify and head. change_attribute(Conf) when is_list(Conf) -> Dir = ?privdir(Conf), - ?line File = filename:join(Dir, "n.LOG"), - ?line No = 4, - ?line del(File, No), % cleanup - ?line B = mk_bytes(60), + File = filename:join(Dir, "n.LOG"), + No = 4, + del(File, No), % cleanup + B = mk_bytes(60), - ?line Q = qlen(), + Q = qlen(), - % test change_notify - ?line {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, + %% test change_notify + {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, {size, {100,No}}]), - ?line {ok, n} = disk_log:open([{name, n}]), % ignored... - ?line ok = disk_log:log_terms(n, [B,B]), - ?line {error, {badarg, notify}} = disk_log:change_notify(n, self(), wrong), - ?line ok = disk_log:change_notify(n, self(), false), - ?line ok = disk_log:change_notify(n, self(), true), - ?line Error1 = {error, {not_owner, _}} = + {ok, n} = disk_log:open([{name, n}]), % ignored... + ok = disk_log:log_terms(n, [B,B]), + {error, {badarg, notify}} = disk_log:change_notify(n, self(), wrong), + ok = disk_log:change_notify(n, self(), false), + ok = disk_log:change_notify(n, self(), true), + Error1 = {error, {not_owner, _}} = disk_log:change_notify(n, none, true), - ?line "The pid" ++ _ = format_error(Error1), - ?line 2 = no_written_items(n), - ?line 0 = users(n), - ?line Parent = self(), - ?line Pid = spawn(fun() -> disk_log:close(n), Parent ! {self(),done} end), - ?line receive {Pid, done} -> ok end, - ?line 0 = users(n), - ?line 1 = length(owners(n)), - - % test change_header - ?line {error, {badarg, head}} = disk_log:change_header(n, none), - ?line {error, {badarg, head}} = + "The pid" ++ _ = format_error(Error1), + 2 = no_written_items(n), + 0 = users(n), + Parent = self(), + Pid = spawn(fun() -> disk_log:close(n), Parent ! {self(),done} end), + receive {Pid, done} -> ok end, + 0 = users(n), + 1 = length(owners(n)), + + %% test change_header + {error, {badarg, head}} = disk_log:change_header(n, none), + {error, {badarg, head}} = disk_log:change_header(n, {head_func, {1,2,3}}), - ?line ok = disk_log:change_header(n, {head, header}), - ?line ok = disk_log:log(n, B), - ?line rec(1, {disk_log, node(), n, {wrap, 0}}), - ?line 4 = no_written_items(n), - ?line ok = disk_log:change_header(n, {head, none}), - ?line ok = disk_log:log(n, B), - ?line rec(1, {disk_log, node(), n, {wrap, 0}}), - ?line 5 = no_written_items(n), - ?line ok = disk_log:change_header(n, + ok = disk_log:change_header(n, {head, header}), + ok = disk_log:log(n, B), + rec(1, {disk_log, node(), n, {wrap, 0}}), + 4 = no_written_items(n), + ok = disk_log:change_header(n, {head, none}), + ok = disk_log:log(n, B), + rec(1, {disk_log, node(), n, {wrap, 0}}), + 5 = no_written_items(n), + ok = disk_log:change_header(n, {head_func, {?MODULE, head_fun, [{ok,header}]}}), - ?line ok = disk_log:log(n, B), - ?line rec(1, {disk_log, node(), n, {wrap, 1}}), - ?line 7 = no_written_items(n), - ?line ok = disk_log:close(n), - ?line {error, no_such_log} = disk_log:close(n), - ?line del(File, No), - ?line file:delete(File), % cleanup - ?line {ok, n} = disk_log:open([{name, n}, {file, File}, {format, external}, + ok = disk_log:log(n, B), + rec(1, {disk_log, node(), n, {wrap, 1}}), + 7 = no_written_items(n), + ok = disk_log:close(n), + {error, no_such_log} = disk_log:close(n), + del(File, No), + file:delete(File), % cleanup + {ok, n} = disk_log:open([{name, n}, {file, File}, {format, external}, {type, halt}]), - ?line {error, {badarg, head}} = disk_log:change_header(n, {head, header}), - ?line ok = disk_log:change_header(n, {head, "header"}), - ?line ok = disk_log:close(n), - ?line file:delete(File), + {error, {badarg, head}} = disk_log:change_header(n, {head, header}), + ok = disk_log:change_header(n, {head, "header"}), + ok = disk_log:close(n), + file:delete(File), - ?line {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, + {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, {size, {100,No}}]), - ?line {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, + {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, {size, {100,No}}]), - ?line ok = disk_log:change_notify(n, self(), true), - ?line ok = disk_log:change_header(n, {head, tjolahopp}), - ?line {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, + ok = disk_log:change_notify(n, self(), true), + ok = disk_log:change_header(n, {head, tjolahopp}), + {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, {size, {100,No}}, {notify, true}]), - ?line ok = disk_log:close(n), - ?line {error, no_such_log} = disk_log:info(n), - ?line Q = qlen(), - ?line del(File, No). + ok = disk_log:close(n), + {error, no_such_log} = disk_log:info(n), + Q = qlen(), + del(File, No). -dist_open(suite) -> []; -dist_open(doc) -> - ["Open a distributed log"]; +%% Open a distributed log. dist_open(Conf) when is_list(Conf) -> - ?line PrivDir = ?privdir(Conf), - ?line true = is_alive(), - - ?line Q = qlen(), - ?line File = filename:join(PrivDir, "n.LOG"), - ?line File1 = filename:join(PrivDir, "n1.LOG"), - ?line No = 3, - ?line file:delete(File), - ?line del(File, No), % cleanup - ?line del(File1, No), % cleanup - ?line B = mk_bytes(60), - - ?line PA = filename:dirname(code:which(?MODULE)), - ?line {ok, Node} = start_node(disk_log, "-pa " ++ PA), - ?line wait_for_ready_net(), + PrivDir = ?privdir(Conf), + true = is_alive(), + + Q = qlen(), + File = filename:join(PrivDir, "n.LOG"), + File1 = filename:join(PrivDir, "n1.LOG"), + No = 3, + file:delete(File), + del(File, No), % cleanup + del(File1, No), % cleanup + B = mk_bytes(60), + + PA = filename:dirname(code:which(?MODULE)), + {ok, Node} = start_node(disk_log, "-pa " ++ PA), + wait_for_ready_net(), %% open non-distributed on this node: - ?line {ok,n} = disk_log:open([{name, n}, {file, File}, {type, halt}, + {ok,n} = disk_log:open([{name, n}, {file, File}, {type, halt}, {distributed, []}]), - ?line Error1 = {error, {halt_log, n}} = disk_log:inc_wrap_file(n), - ?line "The halt log" ++ _ = format_error(Error1), - ?line ok = disk_log:lclose(n), - ?line file:delete(File), + Error1 = {error, {halt_log, n}} = disk_log:inc_wrap_file(n), + "The halt log" ++ _ = format_error(Error1), + ok = disk_log:lclose(n), + file:delete(File), %% open distributed on this node: - ?line {[_],[]} = disk_log:open([{name, n}, {file, File}, {type, halt}, + {[_],[]} = disk_log:open([{name, n}, {file, File}, {type, halt}, {distributed, [node()]}]), %% the error message is ignored: - ?line ok = disk_log:inc_wrap_file(n), - ?line ok = disk_log:close(n), - ?line file:delete(File), + ok = disk_log:inc_wrap_file(n), + ok = disk_log:close(n), + file:delete(File), %% open a wrap log on this node, write something on this node - ?line {[_],[]} = disk_log:open([{name, n}, {file, File}, + {[_],[]} = disk_log:open([{name, n}, {file, File}, {type, wrap}, {size, {50, No}}, {distributed, [node()]}]), - ?line ok = disk_log:log(n, B), - ?line ok = disk_log:close(n), + ok = disk_log:log(n, B), + ok = disk_log:close(n), %% open a wrap log on this node and aother node, write something - ?line {[_],[]} = disk_log:open([{name, n}, {file, File}, + {[_],[]} = disk_log:open([{name, n}, {file, File}, {type, wrap}, {size, {50, No}}, {distributed, [node()]}]), - ?line {[_],[]} = disk_log:open([{name, n}, {file, File1}, + {[_],[]} = disk_log:open([{name, n}, {file, File1}, {type, wrap}, {size, {50, No}}, {distributed, [Node]}]), - ?line ok = disk_log:log(n, B), - ?line ok = rpc:call(Node, disk_log, log, [n, B]), - ?line ok = disk_log:close(n), - ?line del(File, No), - ?line del(File1, No), - ?line file:delete(File), + ok = disk_log:log(n, B), + ok = rpc:call(Node, disk_log, log, [n, B]), + ok = disk_log:close(n), + del(File, No), + del(File1, No), + file:delete(File), %% open a wrap log on this node and another node, use lclose - ?line {[_],[]} = disk_log:open([{name, n}, {file, File}, + {[_],[]} = disk_log:open([{name, n}, {file, File}, {type, wrap}, {size, {50, No}}, {distributed, [node()]}]), - ?line {[_],[]} = disk_log:open([{name, n}, {file, File}, + {[_],[]} = disk_log:open([{name, n}, {file, File}, {type, wrap}, {size, {50, No}}, {distributed, [node()]}, {linkto,none}]), - ?line {[_],[]} = disk_log:open([{name, n}, {file, File1}, + {[_],[]} = disk_log:open([{name, n}, {file, File1}, {type, wrap}, {size, {50, No}}, {distributed, [Node]}]), - ?line [_, _] = distributed(n), - ?line ok = disk_log:lclose(n, Node), - ?line [_] = distributed(n), - ?line ok = disk_log:lclose(n), - ?line ok = disk_log:lclose(n), - ?line {error, no_such_log} = disk_log:info(n), - ?line del(File, No), - ?line del(File1, No), - ?line file:delete(File), - - % open an invalid log file, and see how error are handled - ?line First = "n.LOG.1", - ?line make_file(PrivDir, First, 8), - - ?line {[], [_,_]} = disk_log:open([{name, n}, {file, File}, + [_, _] = distributed(n), + ok = disk_log:lclose(n, Node), + [_] = distributed(n), + ok = disk_log:lclose(n), + ok = disk_log:lclose(n), + {error, no_such_log} = disk_log:info(n), + del(File, No), + del(File1, No), + file:delete(File), + + %% open an invalid log file, and see how error are handled + First = "n.LOG.1", + make_file(PrivDir, First, 8), + + {[], [_,_]} = disk_log:open([{name, n}, {file, File}, {type, wrap}, {size, {50, No}}, {distributed, [Node,node()]}]), - ?line del(File, No), - ?line file:delete(File), + del(File, No), + file:delete(File), - % open a wrap on one other node (not on this node) - ?line {[_],[]} = disk_log:open([{name, n}, {file, File}, + %% open a wrap on one other node (not on this node) + {[_],[]} = disk_log:open([{name, n}, {file, File}, {type, wrap}, {size, {50, No}}, {distributed, [Node]}]), - ?line ok = rpc:call(Node, disk_log, log, [n, B]), - ?line {error, no_such_log} = disk_log:lclose(n), - ?line ok = disk_log:close(n), + ok = rpc:call(Node, disk_log, log, [n, B]), + {error, no_such_log} = disk_log:lclose(n), + ok = disk_log:close(n), - ?line Q = qlen(), + Q = qlen(), - ?line {error, no_such_log} = disk_log:info(n), - ?line del(File, No), - ?line file:delete(File), - ?line stop_node(Node), + {error, no_such_log} = disk_log:info(n), + del(File, No), + file:delete(File), + stop_node(Node), ok. -dist_error_open(suite) -> []; -dist_error_open(doc) -> - ["Open a log distributed and not distributed"]; +%% Open a log distributed and not distributed. dist_error_open(Conf) when is_list(Conf) -> - ?line PrivDir = ?privdir(Conf), - ?line true = is_alive(), - - ?line Q = qlen(), - ?line File = filename:join(PrivDir, "bert.LOG"), - ?line File1 = filename:join(PrivDir, "bert1.LOG"), - ?line No = 3, - ?line file:delete(File), - ?line del(File, No), % cleanup - ?line del(File1, No), % cleanup - - ?line PA = filename:dirname(code:which(?MODULE)), - ?line {ok, Node} = start_node(disk_log, "-pa " ++ PA), - ?line wait_for_ready_net(), - - % open non-distributed on this node: - ?line {ok,n} = disk_log:open([{name, n}, {file, File}, + PrivDir = ?privdir(Conf), + true = is_alive(), + + Q = qlen(), + File = filename:join(PrivDir, "bert.LOG"), + File1 = filename:join(PrivDir, "bert1.LOG"), + No = 3, + file:delete(File), + del(File, No), % cleanup + del(File1, No), % cleanup + + PA = filename:dirname(code:which(?MODULE)), + {ok, Node} = start_node(disk_log, "-pa " ++ PA), + wait_for_ready_net(), + + %% open non-distributed on this node: + {ok,n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, {size, {50, No}}]), - % trying to open distributed on this node (error): - ?line {[],[Error1={ENode,{error,{node_already_open,n}}}]} = + %% trying to open distributed on this node (error): + {[],[Error1={ENode,{error,{node_already_open,n}}}]} = disk_log:open([{name, n}, {file, File}, {type, wrap}, {size, {50, No}}, {distributed, [node()]}]), - ?line true = + true = lists:prefix(lists:flatten(io_lib:format("~p: The distribution", [ENode])), format_error(Error1)), - ?line ok = disk_log:lclose(n), + ok = disk_log:lclose(n), - % open distributed on this node: - ?line {[_],[]} = disk_log:open([{name, n}, {file, File}, + %% open distributed on this node: + {[_],[]} = disk_log:open([{name, n}, {file, File}, {type, wrap}, {size, {50, No}}, {distributed, [node()]}]), - % trying to open non-distributed on this node (error): - ?line {_,{node_already_open,n}} = + %% trying to open non-distributed on this node (error): + {_,{node_already_open,n}} = disk_log:open([{name, n}, {file, File}, {type, wrap}, {size, {50, No}}]), - ?line ok = disk_log:close(n), - ?line Q = qlen(), + ok = disk_log:close(n), + Q = qlen(), - ?line del(File, No), - ?line del(File1, No), - ?line file:delete(File), - ?line stop_node(Node), + del(File, No), + del(File1, No), + file:delete(File), + stop_node(Node), ok. -dist_notify(suite) -> []; -dist_notify(doc) -> - ["Notification from other node"]; +%% Notification from other node. dist_notify(Conf) when is_list(Conf) -> - ?line PrivDir = ?privdir(Conf), - ?line true = is_alive(), + PrivDir = ?privdir(Conf), + true = is_alive(), - ?line File = filename:join(PrivDir, "bert.LOG"), - ?line File1 = filename:join(PrivDir, "bert1.LOG"), - ?line No = 3, - ?line B = mk_bytes(60), - ?line file:delete(File), - ?line file:delete(File1), - ?line del(File, No), % cleanup - ?line del(File1, No), - - ?line PA = filename:dirname(code:which(?MODULE)), - ?line {ok, Node} = start_node(disk_log, "-pa " ++ PA), - ?line wait_for_ready_net(), - - % opening distributed on this node: - ?line {[_],[]} = disk_log:open([{name, n}, {file, File}, {notify, false}, + File = filename:join(PrivDir, "bert.LOG"), + File1 = filename:join(PrivDir, "bert1.LOG"), + No = 3, + B = mk_bytes(60), + file:delete(File), + file:delete(File1), + del(File, No), % cleanup + del(File1, No), + + PA = filename:dirname(code:which(?MODULE)), + {ok, Node} = start_node(disk_log, "-pa " ++ PA), + wait_for_ready_net(), + + %% opening distributed on this node: + {[_],[]} = disk_log:open([{name, n}, {file, File}, {notify, false}, {type, wrap}, {size, {50, No}}, {distributed, [node()]}]), - % opening distributed on other node: - ?line {[_],[]} = disk_log:open([{name, n}, {file, File1}, + %% opening distributed on other node: + {[_],[]} = disk_log:open([{name, n}, {file, File1}, {notify, true}, {linkto, self()}, {type, wrap}, {size, {50, No}}, {distributed, [Node]}]), - ?line disk_log:alog(n, B), - ?line disk_log:alog(n, B), - ?line ok = disk_log:sync(n), - ?line rec(1, {disk_log, Node, n, {wrap, 0}}), - ?line ok = disk_log:close(n), - - ?line del(File, No), - ?line del(File1, No), - ?line file:delete(File), - ?line stop_node(Node), + disk_log:alog(n, B), + disk_log:alog(n, B), + ok = disk_log:sync(n), + rec(1, {disk_log, Node, n, {wrap, 0}}), + ok = disk_log:close(n), + + del(File, No), + del(File1, No), + file:delete(File), + stop_node(Node), ok. -dist_terminate(suite) -> []; -dist_terminate(doc) -> - ["Terminating nodes with distributed logs"]; +%% Terminating nodes with distributed logs. dist_terminate(Conf) when is_list(Conf) -> - ?line Dir = ?privdir(Conf), - ?line true = is_alive(), + Dir = ?privdir(Conf), + true = is_alive(), - ?line File = filename:join(Dir, "n.LOG"), - ?line File1 = filename:join(Dir, "n1.LOG"), + File = filename:join(Dir, "n.LOG"), + File1 = filename:join(Dir, "n1.LOG"), No = 1, del(File, No), % cleanup del(File1, No), % cleanup - ?line PA = filename:dirname(code:which(?MODULE)), - ?line {ok, Node} = start_node(disk_log, "-pa " ++ PA), - ?line wait_for_ready_net(), + PA = filename:dirname(code:which(?MODULE)), + {ok, Node} = start_node(disk_log, "-pa " ++ PA), + wait_for_ready_net(), %% Distributed versions of two of the situations in close_block(/1. %% One of two owners terminates. - ?line Pid1 = spawn_link(?MODULE, lserv, [n]), - ?line Pid2 = spawn_link(?MODULE, lserv, [n]), - ?line {[{_, {ok, n}}], []} = sync_do(Pid1, {dist_open, File, node()}), - ?line {[{_, {ok, n}}], []} = sync_do(Pid2, {dist_open, File1, Node}), - ?line [_] = sync_do(Pid1, owners), - ?line [_] = sync_do(Pid2, owners), - ?line 0 = sync_do(Pid1, users), - ?line 0 = sync_do(Pid2, users), - ?line sync_do(Pid1, terminate), - ?line [_] = sync_do(Pid2, owners), - ?line 0 = sync_do(Pid2, users), - ?line sync_do(Pid2, terminate), - ?line {error, no_such_log} = disk_log:info(n), + Pid1 = spawn_link(?MODULE, lserv, [n]), + Pid2 = spawn_link(?MODULE, lserv, [n]), + {[{_, {ok, n}}], []} = sync_do(Pid1, {dist_open, File, node()}), + {[{_, {ok, n}}], []} = sync_do(Pid2, {dist_open, File1, Node}), + [_] = sync_do(Pid1, owners), + [_] = sync_do(Pid2, owners), + 0 = sync_do(Pid1, users), + 0 = sync_do(Pid2, users), + sync_do(Pid1, terminate), + [_] = sync_do(Pid2, owners), + 0 = sync_do(Pid2, users), + sync_do(Pid2, terminate), + {error, no_such_log} = disk_log:info(n), %% Users terminate (no link...). - ?line Pid3 = spawn_link(?MODULE, lserv, [n]), - ?line Pid4 = spawn_link(?MODULE, lserv, [n]), - ?line {[{_, {ok, n}}], []} = + Pid3 = spawn_link(?MODULE, lserv, [n]), + Pid4 = spawn_link(?MODULE, lserv, [n]), + {[{_, {ok, n}}], []} = sync_do(Pid3, {dist_open, File, none, node()}), - ?line {[{_, {ok, n}}], []} = + {[{_, {ok, n}}], []} = sync_do(Pid4, {dist_open, File1, none, Node}), - ?line [] = sync_do(Pid3, owners), - ?line [] = sync_do(Pid4, owners), - ?line 1 = sync_do(Pid3, users), - ?line 1 = sync_do(Pid4, users), - ?line sync_do(Pid3, terminate), - ?line [] = sync_do(Pid4, owners), - ?line 1 = sync_do(Pid4, users), - ?line sync_do(Pid4, terminate), - ?line ok = disk_log:close(n), % closing all nodes - ?line {error, no_such_log} = disk_log:info(n), + [] = sync_do(Pid3, owners), + [] = sync_do(Pid4, owners), + 1 = sync_do(Pid3, users), + 1 = sync_do(Pid4, users), + sync_do(Pid3, terminate), + [] = sync_do(Pid4, owners), + 1 = sync_do(Pid4, users), + sync_do(Pid4, terminate), + ok = disk_log:close(n), % closing all nodes + {error, no_such_log} = disk_log:info(n), - ?line del(File, No), - ?line del(File1, No), - ?line stop_node(Node), + del(File, No), + del(File1, No), + stop_node(Node), ok. -dist_accessible(suite) -> []; -dist_accessible(doc) -> - ["Accessible logs on nodes"]; +%% Accessible logs on nodes. dist_accessible(Conf) when is_list(Conf) -> - ?line PrivDir = ?privdir(Conf), - - ?line true = is_alive(), - - ?line F1 = filename:join(PrivDir, "a.LOG"), - ?line file:delete(F1), - ?line F2 = filename:join(PrivDir, "b.LOG"), - ?line file:delete(F2), - ?line F3 = filename:join(PrivDir, "c.LOG"), - ?line file:delete(F3), - ?line F4 = filename:join(PrivDir, "d.LOG"), - ?line file:delete(F1), - ?line F5 = filename:join(PrivDir, "e.LOG"), - ?line file:delete(F2), - ?line F6 = filename:join(PrivDir, "f.LOG"), - ?line file:delete(F3), - - ?line {[],[]} = disk_log:accessible_logs(), - ?line {ok, a} = disk_log:open([{name, a}, {type, halt}, {file, F1}]), - ?line {[a],[]} = disk_log:accessible_logs(), - ?line {ok, b} = disk_log:open([{name, b}, {type, halt}, {file, F2}]), - ?line {[a,b],[]} = disk_log:accessible_logs(), - ?line {ok, c} = disk_log:open([{name, c}, {type, halt}, {file, F3}]), - ?line {[a,b,c],[]} = disk_log:accessible_logs(), - - ?line PA = filename:dirname(code:which(?MODULE)), - ?line {ok, Node} = start_node(disk_log, "-pa " ++ PA), - ?line wait_for_ready_net(), - - ?line {[_],[]} = disk_log:open([{name, a}, {file, F4}, {type, halt}, + PrivDir = ?privdir(Conf), + + true = is_alive(), + + F1 = filename:join(PrivDir, "a.LOG"), + file:delete(F1), + F2 = filename:join(PrivDir, "b.LOG"), + file:delete(F2), + F3 = filename:join(PrivDir, "c.LOG"), + file:delete(F3), + F4 = filename:join(PrivDir, "d.LOG"), + file:delete(F1), + F5 = filename:join(PrivDir, "e.LOG"), + file:delete(F2), + F6 = filename:join(PrivDir, "f.LOG"), + file:delete(F3), + + {[],[]} = disk_log:accessible_logs(), + {ok, a} = disk_log:open([{name, a}, {type, halt}, {file, F1}]), + {[a],[]} = disk_log:accessible_logs(), + {ok, b} = disk_log:open([{name, b}, {type, halt}, {file, F2}]), + {[a,b],[]} = disk_log:accessible_logs(), + {ok, c} = disk_log:open([{name, c}, {type, halt}, {file, F3}]), + {[a,b,c],[]} = disk_log:accessible_logs(), + + PA = filename:dirname(code:which(?MODULE)), + {ok, Node} = start_node(disk_log, "-pa " ++ PA), + wait_for_ready_net(), + + {[_],[]} = disk_log:open([{name, a}, {file, F4}, {type, halt}, {distributed, [Node]}]), - ?line {[a,b,c],[]} = disk_log:accessible_logs(), - ?line {[],[a]} = rpc:call(Node, disk_log, accessible_logs, []), - ?line {[_],[]} = disk_log:open([{name, b}, {file, F5}, {type, halt}, + {[a,b,c],[]} = disk_log:accessible_logs(), + {[],[a]} = rpc:call(Node, disk_log, accessible_logs, []), + {[_],[]} = disk_log:open([{name, b}, {file, F5}, {type, halt}, {distributed, [Node]}]), - ?line {[],[a,b]} = rpc:call(Node, disk_log, accessible_logs, []), - ?line {[_],[]} = disk_log:open([{name, c}, {file, F6}, {type, halt}, + {[],[a,b]} = rpc:call(Node, disk_log, accessible_logs, []), + {[_],[]} = disk_log:open([{name, c}, {file, F6}, {type, halt}, {distributed, [Node]}]), - ?line {[],[a,b,c]} = rpc:call(Node, disk_log, accessible_logs, []), - ?line {[a,b,c],[]} = disk_log:accessible_logs(), - ?line ok = disk_log:close(a), - ?line {[b,c],[a]} = disk_log:accessible_logs(), - ?line ok = disk_log:close(b), - ?line {[c],[a,b]} = disk_log:accessible_logs(), - ?line ok = disk_log:close(b), - ?line {[c],[a]} = disk_log:accessible_logs(), - ?line {[],[a,c]} = rpc:call(Node, disk_log, accessible_logs, []), - ?line ok = disk_log:close(c), - ?line {[],[a,c]} = disk_log:accessible_logs(), - ?line ok = disk_log:close(c), - ?line {[],[a]} = disk_log:accessible_logs(), - ?line {[],[a]} = rpc:call(Node, disk_log, accessible_logs, []), - ?line ok = disk_log:close(a), - ?line {[],[]} = disk_log:accessible_logs(), - ?line {[],[]} = rpc:call(Node, disk_log, accessible_logs, []), - - ?line file:delete(F1), - ?line file:delete(F2), - ?line file:delete(F3), - ?line file:delete(F4), - ?line file:delete(F5), - ?line file:delete(F6), - - ?line stop_node(Node), + {[],[a,b,c]} = rpc:call(Node, disk_log, accessible_logs, []), + {[a,b,c],[]} = disk_log:accessible_logs(), + ok = disk_log:close(a), + {[b,c],[a]} = disk_log:accessible_logs(), + ok = disk_log:close(b), + {[c],[a,b]} = disk_log:accessible_logs(), + ok = disk_log:close(b), + {[c],[a]} = disk_log:accessible_logs(), + {[],[a,c]} = rpc:call(Node, disk_log, accessible_logs, []), + ok = disk_log:close(c), + {[],[a,c]} = disk_log:accessible_logs(), + ok = disk_log:close(c), + {[],[a]} = disk_log:accessible_logs(), + {[],[a]} = rpc:call(Node, disk_log, accessible_logs, []), + ok = disk_log:close(a), + {[],[]} = disk_log:accessible_logs(), + {[],[]} = rpc:call(Node, disk_log, accessible_logs, []), + + file:delete(F1), + file:delete(F2), + file:delete(F3), + file:delete(F4), + file:delete(F5), + file:delete(F6), + + stop_node(Node), ok. -dist_deadlock(suite) -> []; -dist_deadlock(doc) -> - ["OTP-4405. Deadlock between two nodes could happen."]; +%% OTP-4405. Deadlock between two nodes could happen. dist_deadlock(Conf) when is_list(Conf) -> - ?line PrivDir = ?privdir(Conf), + PrivDir = ?privdir(Conf), - ?line true = is_alive(), + true = is_alive(), - ?line F1 = filename:join(PrivDir, "a.LOG"), - ?line file:delete(F1), - ?line F2 = filename:join(PrivDir, "b.LOG"), - ?line file:delete(F2), + F1 = filename:join(PrivDir, "a.LOG"), + file:delete(F1), + F2 = filename:join(PrivDir, "b.LOG"), + file:delete(F2), - ?line PA = filename:dirname(code:which(?MODULE)), - ?line {ok, Node1} = start_node(disk_log_node1, "-pa " ++ PA), - ?line {ok, Node2} = start_node(disk_log_node2, "-pa " ++ PA), - ?line wait_for_ready_net(), + PA = filename:dirname(code:which(?MODULE)), + {ok, Node1} = start_node(disk_log_node1, "-pa " ++ PA), + {ok, Node2} = start_node(disk_log_node2, "-pa " ++ PA), + wait_for_ready_net(), Self = self(), Fun1 = fun() -> dist_dl(Node2, a, F1, Self) end, @@ -4476,11 +4375,11 @@ dist_deadlock(Conf) when is_list(Conf) -> receive {P1, a} -> ok end, receive {P2, b} -> ok end, - ?line stop_node(Node1), - ?line stop_node(Node2), + stop_node(Node1), + stop_node(Node2), - ?line file:delete(F1), - ?line file:delete(F2), + file:delete(F1), + file:delete(F2), ok. dist_dl(Node, Name, File, Pid) -> @@ -4491,12 +4390,10 @@ dist_dl(Node, Name, File, Pid) -> Pid ! {self(), Name}, ok. -dist_open2(suite) -> []; -dist_open2(doc) -> - ["OTP-4480. Opening several logs simultaneously."]; +%% OTP-4480. Opening several logs simultaneously. dist_open2(Conf) when is_list(Conf) -> - ?line true = is_alive(), - ?line {ok, _Pg2} = pg2:start(), + true = is_alive(), + {ok, _Pg2} = pg2:start(), dist_open2_1(Conf, 0), dist_open2_1(Conf, 100), @@ -4512,9 +4409,9 @@ dist_open2(Conf) when is_list(Conf) -> %% to open the log. The second one succeeds, and the third one is %% attached. P0 = pps(), - ?line File0 = "n.LOG", - ?line File = filename:join(PrivDir, File0), - ?line make_file(PrivDir, File0, 8), + File0 = "n.LOG", + File = filename:join(PrivDir, File0), + make_file(PrivDir, File0, 8), Parent = self(), F1 = fun() -> R = disk_log:open([{name, Log}, {file, File}, @@ -4528,18 +4425,18 @@ dist_open2(Conf) when is_list(Conf) -> Parent ! {self(), R}, timer:sleep(300) end, - ?line Pid1 = spawn(F1), + Pid1 = spawn(F1), timer:sleep(10), - ?line Pid2 = spawn(F2), - ?line Pid3 = spawn(F2), + Pid2 = spawn(F2), + Pid3 = spawn(F2), - ?line receive {Pid1,R1} -> {[],[_]} = R1 end, - ?line receive {Pid2,R2} -> {[_],[]} = R2 end, - ?line receive {Pid3,R3} -> {[_],[]} = R3 end, + receive {Pid1,R1} -> {[],[_]} = R1 end, + receive {Pid2,R2} -> {[_],[]} = R2 end, + receive {Pid3,R3} -> {[_],[]} = R3 end, timer:sleep(500), - ?line file:delete(File), - ?line true = (P0 == pps()), + file:delete(File), + check_pps(P0), %% This time the first process has a naughty head_func. This test %% does not add very much. Perhaps it should be removed. However, @@ -4560,15 +4457,15 @@ dist_open2(Conf) when is_list(Conf) -> {type,halt}]), Parent ! {self(), R} end, - ?line Pid4 = spawn(F3), + Pid4 = spawn(F3), timer:sleep(10), - ?line Pid5 = spawn(F4), - ?line Pid6 = spawn(F4), + Pid5 = spawn(F4), + Pid6 = spawn(F4), %% The timing is crucial here. - ?line R = case receive {Pid4,R4} -> R4 end of + R = case receive {Pid4,R4} -> R4 end of {error, no_such_log} -> - ?line R5 = receive {Pid5, R5a} -> R5a end, - ?line R6 = receive {Pid6, R6a} -> R6a end, + R5 = receive {Pid5, R5a} -> R5a end, + R6 = receive {Pid6, R6a} -> R6a end, case {R5, R6} of {{repaired, _, _, _}, {ok, Log}} -> ok; {{ok, Log}, {repaired, _, _, _}} -> ok; @@ -4576,16 +4473,16 @@ dist_open2(Conf) when is_list(Conf) -> end, ok; {ok, Log} -> % uninteresting case - ?line receive {Pid5,_R5} -> ok end, - ?line receive {Pid6,_R6} -> ok end, + receive {Pid5,_R5} -> ok end, + receive {Pid6,_R6} -> ok end, {comment, "Timing dependent test did not check anything."} end, timer:sleep(100), - ?line {error, no_such_log} = disk_log:close(Log), + {error, no_such_log} = disk_log:close(Log), file:delete(File), - ?line true = (P0 == pps()), + check_pps(P0), No = 2, Log2 = n2, @@ -4597,7 +4494,7 @@ dist_open2(Conf) when is_list(Conf) -> %% processes should be able to attach to other log without having to %% wait. - ?line {ok,Log} = + {ok,Log} = disk_log:open([{name,Log},{file,File},{type,wrap},{size,{100,No}}]), Pid = spawn(fun() -> receive {HeadPid, start} -> ok end, @@ -4606,15 +4503,15 @@ dist_open2(Conf) when is_list(Conf) -> HeadPid ! {self(), done} end), HeadFunc = {?MODULE, slow_header, [Pid]}, - ?line ok = disk_log:change_header(Log, {head_func, HeadFunc}), - ?line ok = disk_log:inc_wrap_file(Log), % header is written + ok = disk_log:change_header(Log, {head_func, HeadFunc}), + ok = disk_log:inc_wrap_file(Log), % header is written timer:sleep(100), - ?line ok = disk_log:close(Log), + ok = disk_log:close(Log), file:delete(File2), del(File, No), - ?line true = (P0 == pps()), + check_pps(P0), R. @@ -4624,13 +4521,13 @@ dist_open2_1(Conf, Delay) -> Log = n, A0 = [{name,Log},{file,File},{type,halt}], - ?line create_opened_log(File, A0), + create_opened_log(File, A0), P0 = pps(), Log2 = log2, File2 = "log2.LOG", - ?line file:delete(File2), - ?line {ok,Log2} = disk_log:open([{name,Log2},{file,File2},{type,halt}]), + file:delete(File2), + {ok,Log2} = disk_log:open([{name,Log2},{file,File2},{type,halt}]), Parent = self(), F = fun() -> @@ -4638,28 +4535,28 @@ dist_open2_1(Conf, Delay) -> timer:sleep(Delay), Parent ! {self(), R} end, - ?line Pid1 = spawn(F), + Pid1 = spawn(F), timer:sleep(10), - ?line Pid2 = spawn(F), - ?line Pid3 = spawn(F), - ?line {error, no_such_log} = disk_log:log(Log, term), % is repairing now - ?line 0 = qlen(), + Pid2 = spawn(F), + Pid3 = spawn(F), + {error, no_such_log} = disk_log:log(Log, term), % is repairing now + 0 = qlen(), %% The file is already open, so this will not take long. - ?line {ok,Log2} = disk_log:open([{name,Log2},{file,File2},{type,halt}]), - ?line 0 = qlen(), % still repairing - ?line ok = disk_log:close(Log2), - ?line {error, no_such_log} = disk_log:close(Log2), - ?line file:delete(File2), - - ?line receive {Pid1,R1} -> {repaired,_,_,_} = R1 end, - ?line receive {Pid2,R2} -> {ok,_} = R2 end, - ?line receive {Pid3,R3} -> {ok,_} = R3 end, + {ok,Log2} = disk_log:open([{name,Log2},{file,File2},{type,halt}]), + 0 = qlen(), % still repairing + ok = disk_log:close(Log2), + {error, no_such_log} = disk_log:close(Log2), + file:delete(File2), + + receive {Pid1,R1} -> {repaired,_,_,_} = R1 end, + receive {Pid2,R2} -> {ok,_} = R2 end, + receive {Pid3,R3} -> {ok,_} = R3 end, timer:sleep(500), - ?line {error, no_such_log} = disk_log:info(Log), + {error, no_such_log} = disk_log:info(Log), file:delete(File), - ?line true = (P0 == pps()), + check_pps(P0), ok. @@ -4668,18 +4565,18 @@ dist_open2_2(Conf, Delay) -> File = filename:join(Dir, "n.LOG"), Log = n, - ?line PA = filename:dirname(code:which(?MODULE)), - ?line {ok, Node1} = start_node(disk_log_node2, "-pa " ++ PA), - ?line wait_for_ready_net(), + PA = filename:dirname(code:which(?MODULE)), + {ok, Node1} = start_node(disk_log_node2, "-pa " ++ PA), + wait_for_ready_net(), P0 = pps(), A0 = [{name,Log},{file,File},{type,halt}], - ?line create_opened_log(File, A0), + create_opened_log(File, A0), Log2 = log2, File2 = "log2.LOG", - ?line file:delete(File2), - ?line {[{Node1,{ok,Log2}}],[]} = + file:delete(File2), + {[{Node1,{ok,Log2}}],[]} = disk_log:open([{name,Log2},{file,File2},{type,halt}, {distributed,[Node1]}]), @@ -4692,32 +4589,32 @@ dist_open2_2(Conf, Delay) -> Parent ! {self(), R} end, %% And {priority, ...} probably has no effect either. - ?line Pid1 = spawn_opt(F, [{priority, low}]), - % timer:sleep(1), % no guarantee that Pid1 will return {repaired, ...} - ?line Pid2 = spawn_opt(F, [{priority, low}]), - ?line {error, no_such_log} = + Pid1 = spawn_opt(F, [{priority, low}]), + %% timer:sleep(1), % no guarantee that Pid1 will return {repaired, ...} + Pid2 = spawn_opt(F, [{priority, low}]), + {error, no_such_log} = disk_log:log(Log, term), % maybe repairing now - ?line 0 = qlen(), + 0 = qlen(), %% The file is already open, so this will not take long. - ?line {[{Node1,{ok,Log2}}],[]} = + {[{Node1,{ok,Log2}}],[]} = disk_log:open([{name,Log2},{file,File2},{type,halt}, {distributed,[Node1]}]), - ?line 0 = qlen(), % probably still repairing - ?line ok = disk_log:close(Log2), - ?line file:delete(File2), + 0 = qlen(), % probably still repairing + ok = disk_log:close(Log2), + file:delete(File2), - ?line receive {Pid1,R1} -> R1 end, - ?line receive {Pid2,R2} -> R2 end, - ?line case {R1, R2} of + receive {Pid1,R1} -> R1 end, + receive {Pid2,R2} -> R2 end, + case {R1, R2} of {{[{Node1,{repaired,_,_,_}}],[]}, {[{Node1,{ok,Log}}],[]}} -> ok; {{[{Node1,{ok,Log}}],[]}, {[{Node1,{repaired,_,_,_}}],[]}} -> ok end, - ?line true = (P0 == pps()), - ?line stop_node(Node1), + check_pps(P0), + stop_node(Node1), file:delete(File), ok. @@ -4750,168 +4647,50 @@ log_terms(Log, N) -> ok = disk_log:log(Log, {term, N}), log_terms(Log, N-1). -other_groups(suite) -> []; -other_groups(doc) -> - ["OTP-5810. Cope with pg2 groups that are not disk logs."]; +%% OTP-5810. Cope with pg2 groups that are not disk logs. other_groups(Conf) when is_list(Conf) -> - ?line true = is_alive(), - ?line PrivDir = ?privdir(Conf), + true = is_alive(), + PrivDir = ?privdir(Conf), - ?line File = filename:join(PrivDir, "n.LOG"), - ?line file:delete(File), + File = filename:join(PrivDir, "n.LOG"), + file:delete(File), - ?line {[],[]} = disk_log:accessible_logs(), - ?line {[_],[]} = disk_log:open([{name, n}, {file, File}, {type, halt}, + {[],[]} = disk_log:accessible_logs(), + {[_],[]} = disk_log:open([{name, n}, {file, File}, {type, halt}, {distributed, [node()]}]), - ?line {[],[n]} = disk_log:accessible_logs(), + {[],[n]} = disk_log:accessible_logs(), Group = grupp, - ?line pg2:create(Group), - ?line ok = pg2:join(Group, self()), - ?line {[],[n]} = disk_log:accessible_logs(), - ?line [_] = + pg2:create(Group), + ok = pg2:join(Group, self()), + {[],[n]} = disk_log:accessible_logs(), + [_] = lists:filter(fun(P) -> disk_log:pid2name(P) =/= undefined end, erlang:processes()), - ?line pg2:delete(Group), - ?line {[],[n]} = disk_log:accessible_logs(), - ?line ok = disk_log:close(n), - ?line {[],[]} = disk_log:accessible_logs(), - ?line file:delete(File), - - ok. - --define(MAX, 16384). % MAX in disk_log_1.erl -evil(suite) -> []; -evil(doc) -> ["Evil cases such as closed file descriptor port."]; -evil(Conf) when is_list(Conf) -> - Dir = ?privdir(Conf), - File = filename:join(Dir, "n.LOG"), - Log = n, - - %% Not a very thorough test. - - ?line ok = setup_evil_filled_cache_wrap(Log, Dir), - ?line {error, {file_error,_,einval}} = disk_log:log(Log, apa), - ?line ok = disk_log:close(Log), - - ?line ok = setup_evil_filled_cache_halt(Log, Dir), - ?line {error, {file_error,_,einval}} = disk_log:truncate(Log, apa), - ?line ok = stop_evil(Log), - - %% White box test. - file:delete(File), - ?line Ports0 = erlang:ports(), - ?line {ok, Log} = disk_log:open([{name,Log},{file,File},{type,halt}, - {size,?MAX+50},{format,external}]), - ?line [Fd] = erlang:ports() -- Ports0, - ?line {B,_} = x_mk_bytes(30), - ?line ok = disk_log:blog(Log, <<0:(?MAX+1)/unit:8>>), - ?line exit(Fd, kill), - ?line {error, {file_error,_,einval}} = disk_log:blog_terms(Log, [B,B]), - ?line ok= disk_log:close(Log), + pg2:delete(Group), + {[],[n]} = disk_log:accessible_logs(), + ok = disk_log:close(n), + {[],[]} = disk_log:accessible_logs(), file:delete(File), - ?line ok = setup_evil_wrap(Log, Dir), - ?line {error, {file_error,_,einval}} = disk_log:close(Log), - - ?line ok = setup_evil_wrap(Log, Dir), - ?line {error, {file_error,_,einval}} = disk_log:log(Log, apa), - ?line ok = stop_evil(Log), - - ?line ok = setup_evil_halt(Log, Dir), - ?line {error, {file_error,_,einval}} = disk_log:log(Log, apa), - ?line ok = stop_evil(Log), - - ?line ok = setup_evil_wrap(Log, Dir), - ?line {error, {file_error,_,einval}} = disk_log:reopen(Log, apa), - ?line {error, {file_error,_,einval}} = disk_log:reopen(Log, apa), - ?line ok = stop_evil(Log), - - ?line ok = setup_evil_wrap(Log, Dir), - ?line {error, {file_error,_,einval}} = disk_log:reopen(Log, apa), - ?line ok = stop_evil(Log), - - ?line ok = setup_evil_wrap(Log, Dir), - ?line {error, {file_error,_,einval}} = disk_log:inc_wrap_file(Log), - ?line ok = stop_evil(Log), - - ?line ok = setup_evil_wrap(Log, Dir), - ?line {error, {file_error,_,einval}} = disk_log:chunk(Log, start), - ?line ok = stop_evil(Log), - - ?line ok = setup_evil_wrap(Log, Dir), - ?line {error, {file_error,_,einval}} = disk_log:truncate(Log), - ?line ok = stop_evil(Log), - - ?line ok = setup_evil_wrap(Log, Dir), - ?line {error, {file_error,_,einval}} = disk_log:chunk_step(Log, start, 1), - ?line ok = stop_evil(Log), - - io:format("messages: ~p~n", [erlang:process_info(self(), messages)]), - del(File, 2), - file:delete(File), - ok. - -setup_evil_wrap(Log, Dir) -> - setup_evil(Log, [{type,wrap},{size,{100,2}}], Dir). - -setup_evil_halt(Log, Dir) -> - setup_evil(Log, [{type,halt},{size,10000}], Dir). - -setup_evil(Log, Args, Dir) -> - File = filename:join(Dir, lists:concat([Log, ".LOG"])), - file:delete(File), - del(File, 2), - ok = disk_log:start(), - Ports0 = erlang:ports(), - {ok, Log} = disk_log:open([{name,Log},{file,File} | Args]), - [Fd] = erlang:ports() -- Ports0, - exit(Fd, kill), - ok = disk_log:log_terms(n, [<<0:10/unit:8>>]), - timer:sleep(2500), % TIMEOUT in disk_log_1.erl is 2000 - ok. - -stop_evil(Log) -> - {error, _} = disk_log:close(Log), ok. -setup_evil_filled_cache_wrap(Log, Dir) -> - setup_evil_filled_cache(Log, [{type,wrap},{size,{?MAX,2}}], Dir). - -setup_evil_filled_cache_halt(Log, Dir) -> - setup_evil_filled_cache(Log, [{type,halt},{size,infinity}], Dir). - -%% The cache is filled, and the file descriptor port gone. -setup_evil_filled_cache(Log, Args, Dir) -> - File = filename:join(Dir, lists:concat([Log, ".LOG"])), - file:delete(File), - del(File, 2), - ok = disk_log:start(), - Ports0 = erlang:ports(), - {ok, Log} = disk_log:open([{name,Log},{file,File} | Args]), - [Fd] = erlang:ports() -- Ports0, - ok = disk_log:log_terms(n, [<<0:?MAX/unit:8>>]), - exit(Fd, kill), - ok. - -otp_6278(suite) -> []; -otp_6278(doc) -> ["OTP-6278. open/1 creates no status or crash report."]; +%% OTP-6278. open/1 creates no status or crash report. otp_6278(Conf) when is_list(Conf) -> Dir = ?privdir(Conf), File = filename:join(Dir, "no_such_dir/no_such_file"), - ?line error_logger:add_report_handler(?MODULE, self()), - ?line {error, {file_error, _, _}} = + error_logger:add_report_handler(?MODULE, self()), + {error, {file_error, _, _}} = disk_log:open([{name,n},{file,File}]), receive {crash_report,_Pid,Report} -> - ?line io:format("Unexpected: ~p\n", [Report]), - ?line ?t:fail() + io:format("Unexpected: ~p\n", [Report]), + ct:fail(failed) after 1000 -> ok end, - ?line error_logger:delete_report_handler(?MODULE). + error_logger:delete_report_handler(?MODULE). -otp_10131(suite) -> []; -otp_10131(doc) -> ["OTP-10131. head_func type."]; +%% OTP-10131. head_func type. otp_10131(Conf) when is_list(Conf) -> Dir = ?privdir(Conf), Log = otp_10131, @@ -4978,7 +4757,7 @@ copy_wrap_log(FromName, ToName, N, FromDir, ToDir) -> -define(BUFSIZE, 8192). copy_file(Src, Dest) -> - % ?t:format("copying from ~p to ~p~n", [Src, Dest]), + %% io:format("copying from ~p to ~p~n", [Src, Dest]), {ok, InFd} = file:open(Src, [raw, binary, read]), {ok, OutFd} = file:open(Dest, [raw, binary, write]), ok = copy_file1(InFd, OutFd), @@ -5012,10 +4791,59 @@ log(Name, N) -> format_error(E) -> lists:flatten(disk_log:format_error(E)). +check_pps({Ports0,Procs0} = P0) -> + case pps() of + P0 -> + ok; + _ -> + timer:sleep(500), + case pps() of + P0 -> + ok; + {Ports1,Procs1} = P1 -> + case {Ports1 -- Ports0, Procs1 -- Procs0} of + {[], []} -> ok; + {PortsDiff,ProcsDiff} -> + io:format("failure, got ~p~n, expected ~p\n", [P1, P0]), + show("Old port", Ports0 -- Ports1), + show("New port", PortsDiff), + show("Old proc", Procs0 -- Procs1), + show("New proc", ProcsDiff), + ct:fail(failed) + end + end + end. + +show(_S, []) -> + ok; +show(S, [{Pid, Name, InitCall}|Pids]) when is_pid(Pid) -> + io:format("~s: ~w (~w), ~w: ~p~n", + [S, Pid, proc_reg_name(Name), InitCall, + erlang:process_info(Pid)]), + show(S, Pids); +show(S, [{Port, _}|Ports]) when is_port(Port)-> + io:format("~s: ~w: ~p~n", [S, Port, erlang:port_info(Port)]), + show(S, Ports). + pps() -> timer:sleep(100), - {erlang:ports(), lists:filter(fun(P) -> erlang:is_process_alive(P) end, - processes())}. + {port_list(), process_list()}. + +port_list() -> + [{P,safe_second_element(erlang:port_info(P, name))} || + P <- erlang:ports()]. + +process_list() -> + [{P,process_info(P, registered_name), + safe_second_element(process_info(P, initial_call))} || + P <- processes(), erlang:is_process_alive(P)]. + +proc_reg_name({registered_name, Name}) -> Name; +proc_reg_name([]) -> no_reg_name. + +safe_second_element({_,Info}) -> Info; +safe_second_element(Other) -> Other. + qlen() -> {_, {_, N}} = lists:keysearch(message_queue_len, 1, process_info(self())), @@ -5103,50 +4931,15 @@ get_known(Node) -> %% Copied from erl_distribution_SUITE.erl: start_node(Name, Param) -> - ?t:start_node(Name, slave, [{args, Param}]). + test_server:start_node(Name, slave, [{args, Param}]). stop_node(Node) -> - ?t:stop_node(Node). - -%from(H, [H | T]) -> T; -%from(H, [_ | T]) -> from(H, T); -%from(_H, []) -> []. - - -%% Check for NFS cache size, this is called from init_per_testcase() and -%% makes different tests run depending on the size of the NFS cache on -%% VxWorks. Possibly this could be adopted to Windows too, but we seldom use -%% NFS when testing on windows, so I can find better things to do. -%% The port program used simply reads the nfsCacheSize variable on the board. -%% If the board is configured without NFS, the port program will fail to load -%% and this will return 0, which may or may not be the wrong thing to do. - -check_nfs(_Config) -> - 0. + test_server:stop_node(Node). -skip_expand([]) -> - []; -skip_expand([Case | T]) -> - case (catch apply(?MODULE, Case, [suite])) of - {'EXIT', _} -> - [Case | skip_expand(T)]; - [] -> - [Case | skip_expand(T)]; - Res -> - skip_expand(Res) ++ skip_expand(T) - end. - - -skip_list(Config) -> - case check_nfs(Config) of - 0 -> - skip_expand(?SKIP_NO_CACHE); - _ -> - skip_expand(?SKIP_LARGE_CACHE) - end. +%% from(H, [H | T]) -> T; +%% from(H, [_ | T]) -> from(H, T); +%% from(_H, []) -> []. -should_skip(_Test,_Config) -> - false. %%----------------------------------------------------------------- %% The error_logger handler used. @@ -5158,6 +4951,9 @@ init(Tester) -> handle_event({error_report, _GL, {Pid, crash_report, Report}}, Tester) -> Tester ! {crash_report, Pid, Report}, {ok, Tester}; +handle_event({info_msg, _GL, {Pid, F,A}}, Tester) -> + Tester ! {info_msg, Pid, F, A}, + {ok, Tester}; handle_event(_Event, State) -> {ok, State}. diff --git a/lib/kernel/test/disk_log_SUITE_data/wrap_log_test.erl b/lib/kernel/test/disk_log_SUITE_data/wrap_log_test.erl index e5ff70fd49..38449b6bb3 100644 --- a/lib/kernel/test/disk_log_SUITE_data/wrap_log_test.erl +++ b/lib/kernel/test/disk_log_SUITE_data/wrap_log_test.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2009. All Rights Reserved. +%% Copyright Ericsson AB 1998-2016. 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/lib/kernel/test/erl_boot_server_SUITE.erl b/lib/kernel/test/erl_boot_server_SUITE.erl index bb64c01058..1eaa2cf500 100644 --- a/lib/kernel/test/erl_boot_server_SUITE.erl +++ b/lib/kernel/test/erl_boot_server_SUITE.erl @@ -1,24 +1,25 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2011. All Rights Reserved. +%% Copyright Ericsson AB 1996-2016. 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% -module(erl_boot_server_SUITE). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2]). @@ -33,7 +34,9 @@ %% Changed for the new erl_boot_server for R3A by Bjorn Gustavsson. %%----------------------------------------------------------------- -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap,{minutes,1}}]. all() -> [start, start_link, stop, add, delete, responses]. @@ -56,274 +59,262 @@ end_per_group(_GroupName, Config) -> -define(all_ones, {255, 255, 255, 255}). -start(doc) -> "Tests the erl_boot_server:start/1 function."; +%% Tests the erl_boot_server:start/1 function. start(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(50)), - ?line [Host1, Host2|_] = good_hosts(Config), + [Host1, Host2|_] = good_hosts(Config), %% Bad arguments. BadHost = "bad__host", - ?line {error, {badarg, {}}} = erl_boot_server:start({}), - ?line {error, {badarg, atom}} = erl_boot_server:start(atom), - ?line {error, {badarg, [atom, BadHost]}} = + {error, {badarg, {}}} = erl_boot_server:start({}), + {error, {badarg, atom}} = erl_boot_server:start(atom), + {error, {badarg, [atom, BadHost]}} = erl_boot_server:start([atom, BadHost]), - ?line {error, {badarg, [Host1, BadHost]}} = + {error, {badarg, [Host1, BadHost]}} = erl_boot_server:start([Host1, BadHost]), %% Test once. - ?line {ok, Pid1} = erl_boot_server:start([Host1]), - ?line {error, {already_started, Pid1}} = + {ok, Pid1} = erl_boot_server:start([Host1]), + {error, {already_started, Pid1}} = erl_boot_server:start([Host1]), - ?line exit(Pid1, kill), + exit(Pid1, kill), %% Test again. - test_server:sleep(1), - ?line {ok, Pid2} = erl_boot_server:start([Host1, Host2]), - ?line {error, {already_started, Pid2}} = + ct:sleep(1), + {ok, Pid2} = erl_boot_server:start([Host1, Host2]), + {error, {already_started, Pid2}} = erl_boot_server:start([Host1, Host2]), - ?line exit(Pid2, kill), - test_server:sleep(1), + exit(Pid2, kill), + ct:sleep(1), - ?line test_server:timetrap_cancel(Dog), ok. -start_link(doc) -> "Tests the erl_boot_server:start_link/1 function."; +%% Tests the erl_boot_server:start_link/1 function. start_link(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(5)), - ?line [Host1, Host2|_] = good_hosts(Config), + [Host1, Host2|_] = good_hosts(Config), OldFlag = process_flag(trap_exit, true), - ?line {error, {badarg, {}}} = erl_boot_server:start_link({}), - ?line {error, {badarg, atom}} = erl_boot_server:start_link(atom), - ?line BadHost = "bad__host", - ?line {error, {badarg, [atom, BadHost]}} = + {error, {badarg, {}}} = erl_boot_server:start_link({}), + {error, {badarg, atom}} = erl_boot_server:start_link(atom), + BadHost = "bad__host", + {error, {badarg, [atom, BadHost]}} = erl_boot_server:start_link([atom, BadHost]), - ?line {ok, Pid1} = erl_boot_server:start_link([Host1]), - ?line {error, {already_started, Pid1}} = + {ok, Pid1} = erl_boot_server:start_link([Host1]), + {error, {already_started, Pid1}} = erl_boot_server:start_link([Host1]), - ?line shutdown(Pid1), + shutdown(Pid1), - ?line {ok, Pid2} = erl_boot_server:start_link([Host1, Host2]), - ?line {error, {already_started, Pid2}} = + {ok, Pid2} = erl_boot_server:start_link([Host1, Host2]), + {error, {already_started, Pid2}} = erl_boot_server:start_link([Host1, Host2]), - ?line shutdown(Pid2), + shutdown(Pid2), process_flag(trap_exit, OldFlag), - ?line test_server:timetrap_cancel(Dog), ok. -stop(doc) -> "Tests that no processes are left if a boot server is killed."; +%% Tests that no processes are left if a boot server is killed. stop(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(50)), - ?line [Host1|_] = good_hosts(Config), + [Host1|_] = good_hosts(Config), %% Start a boot server and kill it. Make sure that any helper processes %% dies. - % Make sure the inet_gethost_native server is already started, - % otherwise it will make this test fail: - ?line inet:getaddr(localhost, inet), - ?line Before = processes(), - ?line {ok, Pid} = erl_boot_server:start([Host1]), - ?line New = processes() -- [Pid|Before], - ?line exit(Pid, kill), - ?line receive after 100 -> ok end, - ?line case [P || P <- New, is_process_alive(P)] of - [] -> - ok; - NotKilled -> - test_server:fail({not_killed, NotKilled}) - end, - ?line test_server:timetrap_cancel(Dog), + %% Make sure the inet_gethost_native server is already started, + %% otherwise it will make this test fail: + inet:getaddr(localhost, inet), + Before = processes(), + {ok, Pid} = erl_boot_server:start([Host1]), + New = processes() -- [Pid|Before], + exit(Pid, kill), + receive after 100 -> ok end, + case [P || P <- New, is_process_alive(P)] of + [] -> + ok; + NotKilled -> + ct:fail({not_killed, NotKilled}) + end, ok. -add(doc) -> "Tests the erl_boot_server:add/1 function."; +%% Tests the erl_boot_server:add/1 function. add(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(5)), - ?line OldFlag = process_flag(trap_exit, true), - ?line {ok, Pid1} = erl_boot_server:start_link([]), - ?line [] = erl_boot_server:which_slaves(), - ?line [Host1, Host2, Host3|_] = good_hosts(Config), + OldFlag = process_flag(trap_exit, true), + {ok, Pid1} = erl_boot_server:start_link([]), + [] = erl_boot_server:which_slaves(), + [Host1, Host2, Host3|_] = good_hosts(Config), %% Try bad values. - ?line {error, {badarg, {}}} = erl_boot_server:add_slave({}), - ?line {error, {badarg, [atom]}} = erl_boot_server:add_slave([atom]), - ?line BadHost = "bad__host", - ?line {error, {badarg, BadHost}} = erl_boot_server:add_slave(BadHost), - ?line [] = erl_boot_server:which_slaves(), + {error, {badarg, {}}} = erl_boot_server:add_slave({}), + {error, {badarg, [atom]}} = erl_boot_server:add_slave([atom]), + BadHost = "bad__host", + {error, {badarg, BadHost}} = erl_boot_server:add_slave(BadHost), + [] = erl_boot_server:which_slaves(), %% Add good host names. - ?line {ok, Ip1} = inet:getaddr(Host1, inet), - ?line {ok, Ip2} = inet:getaddr(Host2, inet), - ?line {ok, Ip3} = inet:getaddr(Host3, inet), - ?line MIp1 = {?all_ones, Ip1}, - ?line MIp2 = {?all_ones, Ip2}, - ?line MIp3 = {?all_ones, Ip3}, - ?line ok = erl_boot_server:add_slave(Host1), - ?line [MIp1] = erl_boot_server:which_slaves(), - ?line ok = erl_boot_server:add_slave(Host2), - ?line M_Ip1_Ip2 = lists:sort([MIp1, MIp2]), - ?line M_Ip1_Ip2 = lists:sort(erl_boot_server:which_slaves()), - ?line ok = erl_boot_server:add_slave(Host3), - ?line M_Ip1_Ip2_Ip3 = lists:sort([MIp3|M_Ip1_Ip2]), - ?line M_Ip1_Ip2_Ip3 = erl_boot_server:which_slaves(), + {ok, Ip1} = inet:getaddr(Host1, inet), + {ok, Ip2} = inet:getaddr(Host2, inet), + {ok, Ip3} = inet:getaddr(Host3, inet), + MIp1 = {?all_ones, Ip1}, + MIp2 = {?all_ones, Ip2}, + MIp3 = {?all_ones, Ip3}, + ok = erl_boot_server:add_slave(Host1), + [MIp1] = erl_boot_server:which_slaves(), + ok = erl_boot_server:add_slave(Host2), + M_Ip1_Ip2 = lists:sort([MIp1, MIp2]), + M_Ip1_Ip2 = lists:sort(erl_boot_server:which_slaves()), + ok = erl_boot_server:add_slave(Host3), + M_Ip1_Ip2_Ip3 = lists:sort([MIp3|M_Ip1_Ip2]), + M_Ip1_Ip2_Ip3 = erl_boot_server:which_slaves(), %% Add duplicate names. - ?line ok = erl_boot_server:add_slave(Host3), - ?line M_Ip1_Ip2_Ip3 = erl_boot_server:which_slaves(), + ok = erl_boot_server:add_slave(Host3), + M_Ip1_Ip2_Ip3 = erl_boot_server:which_slaves(), %% More bad names. - ?line {error, {badarg, BadHost}} = erl_boot_server:add_slave(BadHost), - ?line M_Ip1_Ip2_Ip3 = erl_boot_server:which_slaves(), + {error, {badarg, BadHost}} = erl_boot_server:add_slave(BadHost), + M_Ip1_Ip2_Ip3 = erl_boot_server:which_slaves(), %% Cleanup. - ?line shutdown(Pid1), - ?line process_flag(trap_exit, OldFlag), - ?line test_server:timetrap_cancel(Dog), + shutdown(Pid1), + process_flag(trap_exit, OldFlag), ok. -delete(doc) -> "Tests the erl_boot_server:delete/1 function."; +%% Tests the erl_boot_server:delete/1 function. delete(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(5)), - ?line OldFlag = process_flag(trap_exit, true), + OldFlag = process_flag(trap_exit, true), - ?line [Host1, Host2, Host3|_] = good_hosts(Config), - ?line {ok, Ip1} = inet:getaddr(Host1, inet), - ?line {ok, Ip2} = inet:getaddr(Host2, inet), - ?line {ok, Ip3} = inet:getaddr(Host3, inet), - ?line MIp1 = {?all_ones, Ip1}, - ?line MIp2 = {?all_ones, Ip2}, - ?line MIp3 = {?all_ones, Ip3}, + [Host1, Host2, Host3|_] = good_hosts(Config), + {ok, Ip1} = inet:getaddr(Host1, inet), + {ok, Ip2} = inet:getaddr(Host2, inet), + {ok, Ip3} = inet:getaddr(Host3, inet), + MIp1 = {?all_ones, Ip1}, + MIp2 = {?all_ones, Ip2}, + MIp3 = {?all_ones, Ip3}, - ?line {ok, Pid1} = erl_boot_server:start_link([Host1, Host2, Host3]), - ?line M_Ip123 = lists:sort([MIp1, MIp2, MIp3]), - ?line M_Ip123 = erl_boot_server:which_slaves(), + {ok, Pid1} = erl_boot_server:start_link([Host1, Host2, Host3]), + M_Ip123 = lists:sort([MIp1, MIp2, MIp3]), + M_Ip123 = erl_boot_server:which_slaves(), %% Do some bad attempts and check that the list of slaves is intact. - ?line {error, {badarg, {}}} = erl_boot_server:delete_slave({}), - ?line {error, {badarg, [atom]}} = erl_boot_server:delete_slave([atom]), - ?line BadHost = "bad__host", - ?line {error, {badarg, BadHost}} = erl_boot_server:delete_slave(BadHost), - ?line M_Ip123 = erl_boot_server:which_slaves(), + {error, {badarg, {}}} = erl_boot_server:delete_slave({}), + {error, {badarg, [atom]}} = erl_boot_server:delete_slave([atom]), + BadHost = "bad__host", + {error, {badarg, BadHost}} = erl_boot_server:delete_slave(BadHost), + M_Ip123 = erl_boot_server:which_slaves(), %% Delete Host2 and make sure it's gone. - ?line ok = erl_boot_server:delete_slave(Host2), - ?line M_Ip13 = lists:sort([MIp1, MIp3]), - ?line M_Ip13 = erl_boot_server:which_slaves(), - - ?line ok = erl_boot_server:delete_slave(Host1), - ?line [MIp3] = erl_boot_server:which_slaves(), - ?line ok = erl_boot_server:delete_slave(Host1), - ?line [MIp3] = erl_boot_server:which_slaves(), - - ?line {error, {badarg, BadHost}} = erl_boot_server:delete_slave(BadHost), - ?line [MIp3] = erl_boot_server:which_slaves(), - - ?line ok = erl_boot_server:delete_slave(Ip3), - ?line [] = erl_boot_server:which_slaves(), - ?line ok = erl_boot_server:delete_slave(Ip3), - ?line [] = erl_boot_server:which_slaves(), - - ?line shutdown(Pid1), - ?line process_flag(trap_exit, OldFlag), - ?line test_server:timetrap_cancel(Dog), + ok = erl_boot_server:delete_slave(Host2), + M_Ip13 = lists:sort([MIp1, MIp3]), + M_Ip13 = erl_boot_server:which_slaves(), + + ok = erl_boot_server:delete_slave(Host1), + [MIp3] = erl_boot_server:which_slaves(), + ok = erl_boot_server:delete_slave(Host1), + [MIp3] = erl_boot_server:which_slaves(), + + {error, {badarg, BadHost}} = erl_boot_server:delete_slave(BadHost), + [MIp3] = erl_boot_server:which_slaves(), + + ok = erl_boot_server:delete_slave(Ip3), + [] = erl_boot_server:which_slaves(), + ok = erl_boot_server:delete_slave(Ip3), + [] = erl_boot_server:which_slaves(), + + shutdown(Pid1), + process_flag(trap_exit, OldFlag), ok. -responses(doc) -> "Tests erl_boot_server responses to slave requests."; +%% Tests erl_boot_server responses to slave requests. responses(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(30)), - ?line process_flag(trap_exit, true), + process_flag(trap_exit, true), %% Copy from inet_boot.hrl EBOOT_PORT = 4368, EBOOT_REQUEST = "EBOOTQ", EBOOT_REPLY = "EBOOTR", - ?line {ok,Host} = inet:gethostname(), - ?line {ok,Ip} = inet:getaddr(Host, inet), + {ok,Host} = inet:gethostname(), + {ok,Ip} = inet:getaddr(Host, inet), ThisVer = erlang:system_info(version), - ?line {ok,BootPid} = erl_boot_server:start_link([Host]), + {ok,BootPid} = erl_boot_server:start_link([Host]), %% Send junk - ?line S1 = open_udp(), - ?line prim_inet:sendto(S1, Ip, EBOOT_PORT, ["0"]), + S1 = open_udp(), + prim_inet:sendto(S1, Ip, EBOOT_PORT, ["0"]), receive What -> - ?line close_udp(S1), - ?line ?t:fail({"got unexpected response",What}) + close_udp(S1), + ct:fail({"got unexpected response",What}) after 100 -> ok end, %% Req from a slave with same erlang vsn. - ?line S2 = open_udp(), - ?line prim_inet:sendto(S2, Ip, EBOOT_PORT, [EBOOT_REQUEST,ThisVer]), + S2 = open_udp(), + prim_inet:sendto(S2, Ip, EBOOT_PORT, [EBOOT_REQUEST,ThisVer]), receive {udp,S2,Ip,_Port1,Resp1} -> - ?line close_udp(S2), - ?line EBOOT_REPLY = string:substr(Resp1, 1, length(EBOOT_REPLY)), - ?line Rest1 = string:substr(Resp1, length(EBOOT_REPLY)+1, length(Resp1)), - ?line [_,_,_ | ThisVer] = Rest1 + close_udp(S2), + EBOOT_REPLY = string:substr(Resp1, 1, length(EBOOT_REPLY)), + Rest1 = string:substr(Resp1, length(EBOOT_REPLY)+1, length(Resp1)), + [_,_,_ | ThisVer] = Rest1 after 2000 -> - ?line close_udp(S2), - ?line ?t:fail("no boot server response; same vsn") + close_udp(S2), + ct:fail("no boot server response; same vsn") end, - + %% Req from a slave with other erlang vsn. - ?line S3 = open_udp(), - ?line prim_inet:sendto(S3, Ip, EBOOT_PORT, [EBOOT_REQUEST,"1.0"]), + S3 = open_udp(), + prim_inet:sendto(S3, Ip, EBOOT_PORT, [EBOOT_REQUEST,"1.0"]), receive Anything -> - ?line close_udp(S3), - ?line ?t:fail({"got unexpected response",Anything}) + close_udp(S3), + ct:fail({"got unexpected response",Anything}) after 100 -> ok end, %% Kill the boot server and wait for it to disappear. - ?line unlink(BootPid), - ?line BootPidMref = erlang:monitor(process, BootPid), - ?line exit(BootPid, kill), + unlink(BootPid), + BootPidMref = erlang:monitor(process, BootPid), + exit(BootPid, kill), receive {'DOWN',BootPidMref,_,_,_} -> ok end, - ?line {ok,BootPid2} = erl_boot_server:start_link(["127.0.0.1"]), + {ok,BootPid2} = erl_boot_server:start_link(["127.0.0.1"]), %% Req from slave with invalid ip address. - ?line S4 = open_udp(), + S4 = open_udp(), Ret = case Ip of {127,0,0,1} -> {comment,"IP address for this host is 127.0.0.1"}; _ -> - ?line prim_inet:sendto(S4, Ip, EBOOT_PORT, - [EBOOT_REQUEST,ThisVer]), + prim_inet:sendto(S4, Ip, EBOOT_PORT, + [EBOOT_REQUEST,ThisVer]), receive Huh -> - ?line close_udp(S4), - ?line ?t:fail({"got unexpected response",Huh}) + close_udp(S4), + ct:fail({"got unexpected response",Huh}) after 100 -> ok end end, - ?line unlink(BootPid2), - ?line exit(BootPid2, kill), + unlink(BootPid2), + exit(BootPid2, kill), %% Now wait for any late unexpected messages. receive Whatever -> - ?line ?t:fail({unexpected_message,Whatever}) + ct:fail({unexpected_message,Whatever}) after 4000 -> - ?line close_udp(S1), - ?line close_udp(S3), - ?line close_udp(S4), + close_udp(S1), + close_udp(S3), + close_udp(S4), ok end, - ?line test_server:timetrap_cancel(Dog), Ret. shutdown(Pid) -> @@ -334,7 +325,7 @@ shutdown(Pid) -> after 1000 -> %% The timeout used to be 1 ms, which could be too short time for the %% SMP emulator on a slow computer with one CPU. - test_server:fail(shutdown) + ct:fail(shutdown) end. good_hosts(_Config) -> @@ -346,10 +337,10 @@ good_hosts(_Config) -> [GoodHost1, GoodHost2, GoodHost3]. open_udp() -> - ?line {ok, S} = prim_inet:open(udp, inet, dgram), - ?line ok = prim_inet:setopts(S, [{mode,list},{active,true}, - {deliver,term},{broadcast,true}]), - ?line {ok,_} = prim_inet:bind(S, {0,0,0,0}, 0), + {ok, S} = prim_inet:open(udp, inet, dgram), + ok = prim_inet:setopts(S, [{mode,list},{active,true}, + {deliver,term},{broadcast,true}]), + {ok,_} = prim_inet:bind(S, {0,0,0,0}, 0), S. close_udp(S) -> diff --git a/lib/kernel/test/erl_distribution_SUITE.erl b/lib/kernel/test/erl_distribution_SUITE.erl index 15c2adc957..5a8bbd56c4 100644 --- a/lib/kernel/test/erl_distribution_SUITE.erl +++ b/lib/kernel/test/erl_distribution_SUITE.erl @@ -1,30 +1,34 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2015. All Rights Reserved. +%% Copyright Ericsson AB 1997-2018. 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/. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at %% -%% 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. +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% -module(erl_distribution_SUITE). -%-define(line_trace, 1). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2]). --export([tick/1, tick_change/1, illegal_nodenames/1, hidden_node/1, +-export([tick/1, tick_change/1, + connect_node/1, + nodenames/1, hostnames/1, + illegal_nodenames/1, hidden_node/1, + setopts/1, table_waste/1, net_setuptime/1, inet_dist_options_options/1, @@ -42,6 +46,8 @@ -export([get_socket_priorities/0, tick_cli_test/1, tick_cli_test1/1, tick_serv_test/2, tick_serv_test1/1, + run_remote_test/1, + setopts_do/2, keep_conn/1, time_ping/1]). -export([init_per_testcase/2, end_per_testcase/2]). @@ -50,7 +56,6 @@ -export([pinger/1]). - -define(DUMMY_NODE,dummy@test01). %%----------------------------------------------------------------- @@ -60,10 +65,14 @@ %% erl -sname master -rsh ctrsh %%----------------------------------------------------------------- -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap,{minutes,4}}]. all() -> - [tick, tick_change, illegal_nodenames, hidden_node, + [tick, tick_change, nodenames, hostnames, illegal_nodenames, + connect_node, + hidden_node, setopts, table_waste, net_setuptime, inet_dist_options_options, {group, monitor_nodes}]. @@ -80,6 +89,7 @@ init_per_suite(Config) -> Config. end_per_suite(_Config) -> + [slave:stop(N) || N <- nodes()], ok. init_per_group(_GroupName, Config) -> @@ -88,29 +98,34 @@ init_per_group(_GroupName, Config) -> end_per_group(_GroupName, Config) -> Config. - +init_per_testcase(TC, Config) when TC == hostnames; + TC == nodenames -> + file:make_dir("hostnames_nodedir"), + file:write_file("hostnames_nodedir/ignore_core_files",""), + Config; init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) -> - Dog=?t:timetrap(?t:minutes(4)), - [{watchdog, Dog}|Config]. + Config. -end_per_testcase(_Func, Config) -> - Dog=?config(watchdog, Config), - ?t:timetrap_cancel(Dog). +end_per_testcase(_Func, _Config) -> + ok. + +connect_node(Config) when is_list(Config) -> + Connected = nodes(connected), + true = net_kernel:connect_node(node()), + Connected = nodes(connected), + ok. -tick(suite) -> []; -tick(doc) -> []; tick(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(120)), PaDir = filename:dirname(code:which(erl_distribution_SUITE)), - + %% First check that the normal case is OK! - ?line {ok, Node} = start_node(dist_test, "-pa " ++ PaDir), + {ok, Node} = start_node(dist_test, "-pa " ++ PaDir), rpc:call(Node, erl_distribution_SUITE, tick_cli_test, [node()]), - + erlang:monitor_node(Node, true), receive {nodedown, Node} -> - test_server:fail("nodedown from other node") + ct:fail("nodedown from other node") after 30000 -> erlang:monitor_node(Node, false), stop_node(Node) @@ -128,20 +143,20 @@ tick(Config) when is_list(Config) -> %% Set the ticktime on the server node to 100 secs so the server %% node doesn't tick the client node within the interval ... - ?line {ok, ServNode} = start_node(dist_test_server, - "-kernel net_ticktime 100 " - "-pa " ++ PaDir), + {ok, ServNode} = start_node(dist_test_server, + "-kernel net_ticktime 100 " + "-pa " ++ PaDir), rpc:call(ServNode, erl_distribution_SUITE, tick_serv_test, [Node, node()]), - ?line {ok, _} = start_node(dist_test, - "-kernel net_ticktime 12 " - "-pa " ++ PaDir), + {ok, _} = start_node(dist_test, + "-kernel net_ticktime 12 " + "-pa " ++ PaDir), rpc:call(Node, erl_distribution_SUITE, tick_cli_test, [ServNode]), - + spawn_link(erl_distribution_SUITE, keep_conn, [Node]), {tick_serv, ServNode} ! {i_want_the_result, self()}, - + monitor_node(ServNode, true), monitor_node(Node, true), @@ -153,56 +168,145 @@ tick(Config) when is_list(Config) -> {tick_test, Error} -> stop_node(ServNode), stop_node(Node), - test_server:fail(Error); + ct:fail(Error); {nodedown, Node} -> stop_node(ServNode), - test_server:fail("client node died"); + ct:fail("client node died"); {nodedown, ServNode} -> stop_node(Node), - test_server:fail("server node died") + ct:fail("server node died") end, - ?line test_server:timetrap_cancel(Dog), ok. -table_waste(doc) -> - ["Checks that pinging nonexistyent nodes does not waste space in distribution table"]; -table_waste(suite) -> - []; +%% Checks that pinging nonexistyent nodes does not waste space in distribution table. table_waste(Config) when is_list(Config) -> - ?line {ok, HName} = inet:gethostname(), + {ok, HName} = inet:gethostname(), F = fun(0,_F) -> []; (N,F) -> - ?line Name = list_to_atom("erl_distribution_"++integer_to_list(N)++ - "@"++HName), - ?line pang = net_adm:ping(Name), - ?line F(N-1,F) + Name = list_to_atom("erl_distribution_"++integer_to_list(N)++ + "@"++HName), + pang = net_adm:ping(Name), + F(N-1,F) end, - ?line F(256,F), - ?line {ok, N} = start_node(erl_distribution_300,""), - ?line stop_node(N), + F(256,F), + {ok, N} = start_node(erl_distribution_300,""), + stop_node(N), ok. - - -illegal_nodenames(doc) -> - ["Test that pinging an illegal nodename does not kill the node"]; -illegal_nodenames(suite) -> - []; +%% Test that starting nodes with different legal name part works, and that illegal +%% ones are filtered +nodenames(Config) when is_list(Config) -> + legal("a1@b"), + legal("a-1@b"), + legal("a_1@b"), + + illegal("cdé@a"), + illegal("te欢st@a"). + +%% Test that starting nodes with different legal host part works, and that illegal +%% ones are filtered +hostnames(Config) when is_list(Config) -> + Host = gethostname(), + legal([$a,$@|atom_to_list(Host)]), + legal("1@b1"), + legal("b@b1-c"), + legal("c@b1_c"), + legal("d@b1#c"), + legal("f@::1"), + legal("g@1:bc3:4e3f:f20:0:1"), + + case file:native_name_encoding() of + latin1 -> ignore; + _ -> legal("e@b1é") + end, + long_hostnames(net_kernel:longnames()), + + illegal("h@testالع"), + illegal("i@языtest"), + illegal("j@te欢st"). + +long_hostnames(true) -> + legal("[email protected]"), + legal("[email protected]"), + legal("[email protected]_c.d"), + legal("[email protected]"), + legal("[email protected]"); +long_hostnames(false) -> + illegal("[email protected]"). + +legal(Name) -> + case test_node(Name) of + started -> + ok; + not_started -> + ct:fail("no ~p node started", [Name]) + end. + +illegal(Name) -> + case test_node(Name, true) of + not_started -> + ok; + started -> + ct:fail("~p node started with illegal name", [Name]) + end. + +test_node(Name) -> + test_node(Name, false). +test_node(Name, Illigal) -> + ProgName = ct:get_progname(), + Command = ProgName ++ " -noinput " ++ long_or_short() ++ Name ++ + " -eval \"net_adm:ping('" ++ atom_to_list(node()) ++ "')\"" ++ + case Illigal of + true -> + " -eval \"timer:sleep(10000),init:stop().\""; + false -> + "" + end, + net_kernel:monitor_nodes(true), + BinCommand = unicode:characters_to_binary(Command, utf8), + Prt = open_port({spawn, BinCommand}, [stream,{cd,"hostnames_nodedir"}]), + Node = list_to_atom(Name), + receive + {nodeup, Node} -> + net_kernel:monitor_nodes(false), + slave:stop(Node), + started + after 5000 -> + net_kernel:monitor_nodes(false), + not_started + end. + +long_or_short() -> + case net_kernel:longnames() of + true -> " -name "; + false -> " -sname " + end. + +% get the localhost's name, depending on the using name policy +gethostname() -> + Hostname = case net_kernel:longnames() of + true-> + net_adm:localhost(); + _-> + {ok, Name}=inet:gethostname(), + Name + end, + list_to_atom(Hostname). + +%% Test that pinging an illegal nodename does not kill the node. illegal_nodenames(Config) when is_list(Config) -> - ?line Dog=?t:timetrap(?t:minutes(2)), PaDir = filename:dirname(code:which(erl_distribution_SUITE)), - ?line {ok, Node}=start_node(illegal_nodenames, "-pa " ++ PaDir), + {ok, Node}=start_node(illegal_nodenames, "-pa " ++ PaDir), monitor_node(Node, true), - ?line RPid=rpc:call(Node, erlang, spawn, - [?MODULE, pinger, [self()]]), + RPid=rpc:call(Node, erlang, spawn, + [?MODULE, pinger, [self()]]), receive {RPid, pinged} -> ok; {nodedown, Node} -> - ?t:fail("Remote node died.") + ct:fail("Remote node died.") end, stop_node(Node), - ?t:timetrap_cancel(Dog), ok. pinger(Starter) -> @@ -212,7 +316,7 @@ pinger(Starter) -> ok. -net_setuptime(doc) -> ["Test that you can set the net_setuptime properly"]; +%% Test that you can set the net_setuptime properly. net_setuptime(Config) when is_list(Config) -> %% In this test case, we reluctantly accept shorter times than the given %% setup time, because the connection attempt can end in a @@ -220,29 +324,28 @@ net_setuptime(Config) when is_list(Config) -> Res0 = do_test_setuptime("2"), io:format("Res0 = ~p", [Res0]), - ?line true = (Res0 =< 4000), + true = (Res0 =< 4000), Res1 = do_test_setuptime("0.3"), io:format("Res1 = ~p", [Res1]), - ?line true = (Res1 =< 500), + true = (Res1 =< 500), ok. do_test_setuptime(Setuptime) when is_list(Setuptime) -> - ?line PaDir = filename:dirname(code:which(?MODULE)), - ?line {ok, Node} = start_node(dist_setuptime_test, "-pa " ++ PaDir ++ - " -kernel net_setuptime " ++ Setuptime), - ?line Res = rpc:call(Node,?MODULE,time_ping,[?DUMMY_NODE]), - ?line stop_node(Node), + PaDir = filename:dirname(code:which(?MODULE)), + {ok, Node} = start_node(dist_setuptime_test, "-pa " ++ PaDir ++ + " -kernel net_setuptime " ++ Setuptime), + Res = rpc:call(Node,?MODULE,time_ping,[?DUMMY_NODE]), + stop_node(Node), Res. time_ping(Node) -> - T0 = erlang:now(), + T0 = erlang:monotonic_time(), pang = net_adm:ping(Node), - T1 = erlang:now(), - time_diff(T0,T1). - + T1 = erlang:monotonic_time(), + erlang:convert_time_unit(T1 - T0, native, millisecond). %% Keep the connection with the client node up. -%% This is neccessary as the client node runs with much shorter +%% This is necessary as the client node runs with much shorter %% tick time !! keep_conn(Node) -> sleep(1), @@ -276,13 +379,15 @@ tick_cli_test1(Node) -> erlang:monitor_node(Node, true), sleep(2), rpc:call(Node, erlang, time, []), %% simulate action on the connection - T1 = now(), + T1 = erlang:monotonic_time(), receive {nodedown, Node} -> - T2 = now(), + T2 = erlang:monotonic_time(), receive {whats_the_result, From} -> - case time_diff(T1, T2) of + Diff = erlang:convert_time_unit(T2-T1, native, + millisecond), + case Diff of T when T > 8000, T < 16000 -> From ! {tick_test, T}; T -> @@ -293,161 +398,319 @@ tick_cli_test1(Node) -> end end. +setopts(Config) when is_list(Config) -> + register(setopts_regname, self()), + [N1,N2,N3,N4] = get_nodenames(4, setopts), + + {_N1F,Port1} = start_node_unconnected(N1, ?MODULE, run_remote_test, + ["setopts_do", atom_to_list(node()), "1", "ping"]), + 0 = wait_for_port_exit(Port1), + + {_N2F,Port2} = start_node_unconnected(N2, ?MODULE, run_remote_test, + ["setopts_do", atom_to_list(node()), "2", "ping"]), + 0 = wait_for_port_exit(Port2), + + {ok, LSock} = gen_tcp:listen(0, [{packet,2}, {active,false}]), + {ok, LTcpPort} = inet:port(LSock), + + {N3F,Port3} = start_node_unconnected(N3, ?MODULE, run_remote_test, + ["setopts_do", atom_to_list(node()), + "1", integer_to_list(LTcpPort)]), + wait_and_connect(LSock, N3F, Port3), + 0 = wait_for_port_exit(Port3), + + {N4F,Port4} = start_node_unconnected(N4, ?MODULE, run_remote_test, + ["setopts_do", atom_to_list(node()), + "2", integer_to_list(LTcpPort)]), + wait_and_connect(LSock, N4F, Port4), + 0 = wait_for_port_exit(Port4), + + ok. + +wait_and_connect(LSock, NodeName, NodePort) -> + {ok, Sock} = gen_tcp:accept(LSock), + {ok, "Connect please"} = gen_tcp:recv(Sock, 0), + flush_from_port(NodePort), + pong = net_adm:ping(NodeName), + gen_tcp:send(Sock, "Connect done"), + gen_tcp:close(Sock). + + +flush_from_port(Port) -> + flush_from_port(Port, 10). + +flush_from_port(Port, Timeout) -> + receive + {Port,{data,String}} -> + io:format("~p: ~s\n", [Port, String]), + flush_from_port(Port, Timeout) + after Timeout -> + timeout + end. + +wait_for_port_exit(Port) -> + case (receive M -> M end) of + {Port,{exit_status,Status}} -> + Status; + {Port,{data,String}} -> + io:format("~p: ~s\n", [Port, String]), + wait_for_port_exit(Port) + end. + +run_remote_test([FuncStr, TestNodeStr | Args]) -> + Status = try + io:format("Node ~p started~n", [node()]), + TestNode = list_to_atom(TestNodeStr), + io:format("Node ~p spawning function ~p~n", [node(), FuncStr]), + {Pid,Ref} = spawn_monitor(?MODULE, list_to_atom(FuncStr), [TestNode, Args]), + io:format("Node ~p waiting for function ~p~n", [node(), FuncStr]), + receive + {'DOWN', Ref, process, Pid, normal} -> + 0; + Other -> + io:format("Node ~p got unexpected msg: ~p\n",[node(), Other]), + 1 + end + catch + C:E:S -> + io:format("Node ~p got EXCEPTION ~p:~p\nat ~p\n", + [node(), C, E, S]), + 2 + end, + io:format("Node ~p doing halt(~p).\n",[node(), Status]), + erlang:halt(Status). + +% Do the actual test on the remote node +setopts_do(TestNode, [OptNr, ConnectData]) -> + [] = nodes(), + {Opt, Val} = opt_from_nr(OptNr), + ok = net_kernel:setopts(new, [{Opt, Val}]), + + [] = nodes(), + {error, noconnection} = net_kernel:getopts(TestNode, [Opt]), + + case ConnectData of + "ping" -> % We connect + net_adm:ping(TestNode); + TcpPort -> % Other connect + {ok, Sock} = gen_tcp:connect("localhost", list_to_integer(TcpPort), + [{active,false},{packet,2}]), + ok = gen_tcp:send(Sock, "Connect please"), + {ok, "Connect done"} = gen_tcp:recv(Sock, 0), + gen_tcp:close(Sock) + end, + [TestNode] = nodes(), + {ok, [{Opt,Val}]} = net_kernel:getopts(TestNode, [Opt]), + {error, noconnection} = net_kernel:getopts('pixie@fairyland', [Opt]), + + NewVal = change_val(Val), + ok = net_kernel:setopts(TestNode, [{Opt, NewVal}]), + {ok, [{Opt,NewVal}]} = net_kernel:getopts(TestNode, [Opt]), + + ok = net_kernel:setopts(TestNode, [{Opt, Val}]), + {ok, [{Opt,Val}]} = net_kernel:getopts(TestNode, [Opt]), + + ok. + +opt_from_nr("1") -> {nodelay, true}; +opt_from_nr("2") -> {nodelay, false}. + +change_val(true) -> false; +change_val(false) -> true. + +start_node_unconnected(Name, Mod, Func, Args) -> + FullName = full_node_name(Name), + CmdLine = mk_node_cmdline(Name,Mod,Func,Args), + io:format("Starting node ~p: ~s~n", [FullName, CmdLine]), + case open_port({spawn, CmdLine}, [exit_status]) of + Port when is_port(Port) -> + {FullName, Port}; + Error -> + exit({failed_to_start_node, FullName, Error}) + end. -tick_change(doc) -> ["OTP-4255"]; -tick_change(suite) -> []; +full_node_name(PreName) -> + HostSuffix = lists:dropwhile(fun ($@) -> false; (_) -> true end, + atom_to_list(node())), + list_to_atom(atom_to_list(PreName) ++ HostSuffix). + +mk_node_cmdline(Name,Mod,Func,Args) -> + Static = "-noinput", + Pa = filename:dirname(code:which(?MODULE)), + Prog = case catch init:get_argument(progname) of + {ok,[[P]]} -> P; + _ -> exit(no_progname_argument_found) + end, + NameSw = case net_kernel:longnames() of + false -> "-sname "; + true -> "-name "; + _ -> exit(not_distributed_node) + end, + {ok, Pwd} = file:get_cwd(), + NameStr = atom_to_list(Name), + Prog ++ " " + ++ Static ++ " " + ++ NameSw ++ " " ++ NameStr + ++ " -pa " ++ Pa + ++ " -env ERL_CRASH_DUMP " ++ Pwd ++ "/erl_crash_dump." ++ NameStr + ++ " -setcookie " ++ atom_to_list(erlang:get_cookie()) + ++ " -run " ++ atom_to_list(Mod) ++ " " ++ atom_to_list(Func) + ++ " " ++ string:join(Args, " "). + + +%% OTP-4255. tick_change(Config) when is_list(Config) -> - ?line PaDir = filename:dirname(code:which(?MODULE)), - ?line [BN, CN] = get_nodenames(2, tick_change), - ?line DefaultTT = net_kernel:get_net_ticktime(), - ?line unchanged = net_kernel:set_net_ticktime(DefaultTT, 60), - ?line case DefaultTT of - I when is_integer(I) -> ?line ok; - _ -> ?line ?t:fail(DefaultTT) - end, - - % In case other nodes are connected + PaDir = filename:dirname(code:which(?MODULE)), + [BN, CN] = get_nodenames(2, tick_change), + DefaultTT = net_kernel:get_net_ticktime(), + unchanged = net_kernel:set_net_ticktime(DefaultTT, 60), + case DefaultTT of + I when is_integer(I) -> ok; + _ -> ct:fail(DefaultTT) + end, + + %% In case other nodes are connected case nodes(connected) of - [] -> ?line net_kernel:set_net_ticktime(10, 0); - _ -> ?line rpc:multicall(nodes([this, connected]), net_kernel, - set_net_ticktime, [10, 5]) + [] -> net_kernel:set_net_ticktime(10, 0); + _ -> rpc:multicall(nodes([this, connected]), net_kernel, + set_net_ticktime, [10, 5]) end, - ?line wait_until(fun () -> 10 == net_kernel:get_net_ticktime() end), - ?line {ok, B} = start_node(BN, "-kernel net_ticktime 10 -pa " ++ PaDir), - ?line {ok, C} = start_node(CN, "-kernel net_ticktime 10 -hidden -pa " - ++ PaDir), + wait_until(fun () -> 10 == net_kernel:get_net_ticktime() end), + {ok, B} = start_node(BN, "-kernel net_ticktime 10 -pa " ++ PaDir), + {ok, C} = start_node(CN, "-kernel net_ticktime 10 -hidden -pa " + ++ PaDir), - ?line OTE = process_flag(trap_exit, true), + OTE = process_flag(trap_exit, true), case catch begin - ?line run_tick_change_test(B, C, 10, 1, PaDir), - ?line run_tick_change_test(B, C, 1, 10, PaDir) + run_tick_change_test(B, C, 10, 1, PaDir), + run_tick_change_test(B, C, 1, 10, PaDir) end of {'EXIT', Reason} -> - ?line stop_node(B), - ?line stop_node(C), + stop_node(B), + stop_node(C), %% In case other nodes are connected case nodes(connected) of - [] -> ?line net_kernel:set_net_ticktime(DefaultTT, 0); - _ -> ?line rpc:multicall(nodes([this, connected]), net_kernel, - set_net_ticktime, [DefaultTT, 10]) + [] -> net_kernel:set_net_ticktime(DefaultTT, 0); + _ -> rpc:multicall(nodes([this, connected]), net_kernel, + set_net_ticktime, [DefaultTT, 10]) end, - ?line wait_until(fun () -> - DefaultTT == net_kernel:get_net_ticktime() - end), - ?line process_flag(trap_exit, OTE), - ?t:fail(Reason); + wait_until(fun () -> + DefaultTT == net_kernel:get_net_ticktime() + end), + process_flag(trap_exit, OTE), + ct:fail(Reason); _ -> ok end, - ?line process_flag(trap_exit, OTE), - ?line stop_node(B), - ?line stop_node(C), + process_flag(trap_exit, OTE), + stop_node(B), + stop_node(C), - % In case other nodes are connected + %% In case other nodes are connected case nodes(connected) of - [] -> ?line net_kernel:set_net_ticktime(DefaultTT, 0); - _ -> ?line rpc:multicall(nodes([this, connected]), net_kernel, - set_net_ticktime, [DefaultTT, 5]) + [] -> net_kernel:set_net_ticktime(DefaultTT, 0); + _ -> rpc:multicall(nodes([this, connected]), net_kernel, + set_net_ticktime, [DefaultTT, 5]) end, - ?line wait_until(fun () -> DefaultTT == net_kernel:get_net_ticktime() end), - ?line ok. + wait_until(fun () -> DefaultTT == net_kernel:get_net_ticktime() end), + ok. wait_for_nodedowns(Tester, Ref) -> receive {nodedown, Node} -> - ?t:format("~p~n", [{node(), {nodedown, Node}}]), - ?line Tester ! {Ref, {node(), {nodedown, Node}}} + io:format("~p~n", [{node(), {nodedown, Node}}]), + Tester ! {Ref, {node(), {nodedown, Node}}} end, wait_for_nodedowns(Tester, Ref). run_tick_change_test(B, C, PrevTT, TT, PaDir) -> - ?line [DN, EN] = get_nodenames(2, tick_change), - - ?line Tester = self(), - ?line Ref = make_ref(), - ?line MonitorNodes = fun (Nodes) -> - ?line lists:foreach( - fun (N) -> - ?line monitor_node(N,true) - end, - Nodes), - wait_for_nodedowns(Tester, Ref) - end, - - ?line {ok, D} = start_node(DN, "-kernel net_ticktime " - ++ integer_to_list(PrevTT) ++ " -pa " ++ PaDir), - - ?line NMA = spawn_link(fun () -> MonitorNodes([B, C, D]) end), - ?line NMB = spawn_link(B, fun () -> MonitorNodes([node(), C, D]) end), - ?line NMC = spawn_link(C, fun () -> MonitorNodes([node(), B, D]) end), - - ?line MaxTT = case PrevTT > TT of - true -> ?line PrevTT; - false -> ?line TT - end, + [DN, EN] = get_nodenames(2, tick_change), + + Tester = self(), + Ref = make_ref(), + MonitorNodes = fun (Nodes) -> + lists:foreach( + fun (N) -> + monitor_node(N,true) + end, + Nodes), + wait_for_nodedowns(Tester, Ref) + end, + + {ok, D} = start_node(DN, "-kernel net_ticktime " + ++ integer_to_list(PrevTT) ++ " -pa " ++ PaDir), + + NMA = spawn_link(fun () -> MonitorNodes([B, C, D]) end), + NMB = spawn_link(B, fun () -> MonitorNodes([node(), C, D]) end), + NMC = spawn_link(C, fun () -> MonitorNodes([node(), B, D]) end), + + MaxTT = case PrevTT > TT of + true -> PrevTT; + false -> TT + end, - ?line CheckResult = make_ref(), - ?line spawn_link(fun () -> - receive - after (25 + MaxTT)*1000 -> - Tester ! CheckResult - end - end), + CheckResult = make_ref(), + spawn_link(fun () -> + receive + after (25 + MaxTT)*1000 -> + Tester ! CheckResult + end + end), - % In case other nodes than these are connected + %% In case other nodes than these are connected case nodes(connected) -- [B, C, D] of - [] -> ?line ok; - OtherNodes -> ?line rpc:multicall(OtherNodes, net_kernel, - set_net_ticktime, [TT, 20]) + [] -> ok; + OtherNodes -> rpc:multicall(OtherNodes, net_kernel, + set_net_ticktime, [TT, 20]) end, - ?line change_initiated = net_kernel:set_net_ticktime(TT,20), - ?line {ongoing_change_to,_} = net_kernel:set_net_ticktime(TT,20), - ?line sleep(3), - ?line change_initiated = rpc:call(B,net_kernel,set_net_ticktime,[TT,15]), - ?line sleep(7), - ?line change_initiated = rpc:call(C,net_kernel,set_net_ticktime,[TT,10]), - - ?line {ok, E} = start_node(EN, "-kernel net_ticktime " - ++ integer_to_list(TT) ++ " -pa " ++ PaDir), - ?line NME = spawn_link(E, fun () -> MonitorNodes([node(), B, C, D]) end), - ?line NMA2 = spawn_link(fun () -> MonitorNodes([E]) end), - ?line NMB2 = spawn_link(B, fun () -> MonitorNodes([E]) end), - ?line NMC2 = spawn_link(C, fun () -> MonitorNodes([E]) end), - - receive CheckResult -> ?line ok end, - - ?line unlink(NMA), exit(NMA, kill), - ?line unlink(NMB), exit(NMB, kill), - ?line unlink(NMC), exit(NMC, kill), - ?line unlink(NME), exit(NME, kill), - ?line unlink(NMA2), exit(NMA2, kill), - ?line unlink(NMB2), exit(NMB2, kill), - ?line unlink(NMC2), exit(NMC2, kill), + change_initiated = net_kernel:set_net_ticktime(TT,20), + {ongoing_change_to,_} = net_kernel:set_net_ticktime(TT,20), + sleep(3), + change_initiated = rpc:call(B,net_kernel,set_net_ticktime,[TT,15]), + sleep(7), + change_initiated = rpc:call(C,net_kernel,set_net_ticktime,[TT,10]), + + {ok, E} = start_node(EN, "-kernel net_ticktime " + ++ integer_to_list(TT) ++ " -pa " ++ PaDir), + NME = spawn_link(E, fun () -> MonitorNodes([node(), B, C, D]) end), + NMA2 = spawn_link(fun () -> MonitorNodes([E]) end), + NMB2 = spawn_link(B, fun () -> MonitorNodes([E]) end), + NMC2 = spawn_link(C, fun () -> MonitorNodes([E]) end), + + receive CheckResult -> ok end, + + unlink(NMA), exit(NMA, kill), + unlink(NMB), exit(NMB, kill), + unlink(NMC), exit(NMC, kill), + unlink(NME), exit(NME, kill), + unlink(NMA2), exit(NMA2, kill), + unlink(NMB2), exit(NMB2, kill), + unlink(NMC2), exit(NMC2, kill), %% The node not changing ticktime should have been disconnected from the %% other nodes - receive {Ref, {Node, {nodedown, D}}} when Node == node() -> ?line ok - after 0 -> ?line exit({?LINE, no_nodedown}) + receive {Ref, {Node, {nodedown, D}}} when Node == node() -> ok + after 0 -> exit({?LINE, no_nodedown}) end, - receive {Ref, {B, {nodedown, D}}} -> ?line ok - after 0 -> ?line exit({?LINE, no_nodedown}) + receive {Ref, {B, {nodedown, D}}} -> ok + after 0 -> exit({?LINE, no_nodedown}) end, - receive {Ref, {C, {nodedown, D}}} -> ?line ok - after 0 -> ?line exit({?LINE, no_nodedown}) + receive {Ref, {C, {nodedown, D}}} -> ok + after 0 -> exit({?LINE, no_nodedown}) end, - receive {Ref, {E, {nodedown, D}}} -> ?line ok - after 0 -> ?line exit({?LINE, no_nodedown}) + receive {Ref, {E, {nodedown, D}}} -> ok + after 0 -> exit({?LINE, no_nodedown}) end, %% No other connections should have been broken receive {Ref, Reason} -> - ?line stop_node(E), - ?line exit({?LINE, Reason}); + stop_node(E), + exit({?LINE, Reason}); {'EXIT', Pid, Reason} when Pid == NMA; Pid == NMB; Pid == NMC; @@ -455,70 +718,65 @@ run_tick_change_test(B, C, PrevTT, TT, PaDir) -> Pid == NMA2; Pid == NMB2; Pid == NMC2 -> - ?line stop_node(E), + stop_node(E), - ?line exit({?LINE, {node(Pid), Reason}}) + exit({?LINE, {node(Pid), Reason}}) after 0 -> - ?line TT = net_kernel:get_net_ticktime(), - ?line TT = rpc:call(B, net_kernel, get_net_ticktime, []), - ?line TT = rpc:call(C, net_kernel, get_net_ticktime, []), - ?line TT = rpc:call(E, net_kernel, get_net_ticktime, []), - ?line stop_node(E), - ?line ok + TT = net_kernel:get_net_ticktime(), + TT = rpc:call(B, net_kernel, get_net_ticktime, []), + TT = rpc:call(C, net_kernel, get_net_ticktime, []), + TT = rpc:call(E, net_kernel, get_net_ticktime, []), + stop_node(E), + ok end. %% %% Basic tests of hidden node. %% -hidden_node(doc) -> - ["Basic test of hidden node"]; -hidden_node(suite) -> - []; +%% Basic test of hidden node. hidden_node(Config) when is_list(Config) -> - ?line Dog = ?t:timetrap(?t:seconds(40)), PaDir = filename:dirname(code:which(?MODULE)), VArgs = "-pa " ++ PaDir, HArgs = "-hidden -pa " ++ PaDir, - ?line {ok, V} = start_node(visible_node, VArgs), + {ok, V} = start_node(visible_node, VArgs), VMN = start_monitor_nodes_proc(V), - ?line {ok, H} = start_node(hidden_node, HArgs), - % Connect visible_node -> hidden_node + {ok, H} = start_node(hidden_node, HArgs), + %% Connect visible_node -> hidden_node connect_nodes(V, H), test_nodes(V, H), stop_node(H), sleep(5), check_monitor_nodes_res(VMN, H), stop_node(V), - ?line {ok, H} = start_node(hidden_node, HArgs), + {ok, H} = start_node(hidden_node, HArgs), HMN = start_monitor_nodes_proc(H), - ?line {ok, V} = start_node(visible_node, VArgs), - % Connect hidden_node -> visible_node + {ok, V} = start_node(visible_node, VArgs), + %% Connect hidden_node -> visible_node connect_nodes(H, V), test_nodes(V, H), stop_node(V), sleep(5), check_monitor_nodes_res(HMN, V), stop_node(H), - ?line ?t:timetrap_cancel(Dog), ok. connect_nodes(A, B) -> - % Check that they haven't already connected. - ?line false = lists:member(A, rpc:call(B, erlang, nodes, [connected])), - ?line false = lists:member(B, rpc:call(A, erlang, nodes, [connected])), - % Connect them. - ?line pong = rpc:call(A, net_adm, ping, [B]). - + %% Check that they haven't already connected. + false = lists:member(A, rpc:call(B, erlang, nodes, [connected])), + false = lists:member(B, rpc:call(A, erlang, nodes, [connected])), + %% Connect them. + pong = rpc:call(A, net_adm, ping, [B]). + test_nodes(V, H) -> - % No nodes should be visible on hidden_node - ?line [] = rpc:call(H, erlang, nodes, []), - % visible_node should be hidden on hidden_node - ?line true = lists:member(V, rpc:call(H, erlang, nodes, [hidden])), - % hidden_node node shouldn't be visible on visible_node - ?line false = lists:member(H, rpc:call(V, erlang, nodes, [])), - % hidden_node should be hidden on visible_node - ?line true = lists:member(H, rpc:call(V, erlang, nodes, [hidden])). + %% No nodes should be visible on hidden_node + [] = rpc:call(H, erlang, nodes, []), + %% visible_node should be hidden on hidden_node + true = lists:member(V, rpc:call(H, erlang, nodes, [hidden])), + %% hidden_node node shouldn't be visible on visible_node + false = lists:member(H, rpc:call(V, erlang, nodes, [])), + %% hidden_node should be hidden on visible_node + true = lists:member(H, rpc:call(V, erlang, nodes, [hidden])). mn_loop(MNs) -> receive @@ -546,21 +804,19 @@ start_monitor_nodes_proc(Node) -> ok end, Pid. - + check_monitor_nodes_res(Pid, Node) -> Ref = make_ref(), Pid ! {monitor_nodes_result, Ref, self()}, receive {Ref, MNs} -> - ?line false = lists:keysearch(Node, 2, MNs) + false = lists:keysearch(Node, 2, MNs) end. -inet_dist_options_options(suite) -> []; -inet_dist_options_options(doc) -> - ["Check the kernel inet_dist_{listen,connect}_options options"]; +%% Check the kernel inet_dist_{listen,connect}_options options. inet_dist_options_options(Config) when is_list(Config) -> Prio = 1, case gen_udp:open(0, [{priority,Prio}]) of @@ -569,7 +825,7 @@ inet_dist_options_options(Config) when is_list(Config) -> {ok,[{priority,Prio}]} -> ok = gen_udp:close(Socket), do_inet_dist_options_options(Prio); - _ -> + _ -> ok = gen_udp:close(Socket), {skip, "Can not set priority "++integer_to_list(Prio)++ @@ -593,25 +849,25 @@ do_inet_dist_options_options(Prio) -> "-hidden " "-kernel inet_dist_connect_options "++PriorityString++" " "-kernel inet_dist_listen_options "++PriorityString, - ?line {ok,Node1} = + {ok,Node1} = start_node(inet_dist_options_1, InetDistOptions), - ?line {ok,Node2} = + {ok,Node2} = start_node(inet_dist_options_2, InetDistOptions), %% - ?line pong = + pong = rpc:call(Node1, net_adm, ping, [Node2]), - ?line PrioritiesNode1 = + PrioritiesNode1 = rpc:call(Node1, ?MODULE, get_socket_priorities, []), - ?line PrioritiesNode2 = + PrioritiesNode2 = rpc:call(Node2, ?MODULE, get_socket_priorities, []), - ?line ?t:format("PrioritiesNode1 = ~p", [PrioritiesNode1]), - ?line ?t:format("PrioritiesNode2 = ~p", [PrioritiesNode2]), - ?line Elevated = [P || P <- PrioritiesNode1, P =:= Prio], - ?line Elevated = [P || P <- PrioritiesNode2, P =:= Prio], - ?line [_|_] = Elevated, + io:format("PrioritiesNode1 = ~p", [PrioritiesNode1]), + io:format("PrioritiesNode2 = ~p", [PrioritiesNode2]), + Elevated = [P || P <- PrioritiesNode1, P =:= Prio], + Elevated = [P || P <- PrioritiesNode2, P =:= Prio], + [_|_] = Elevated, %% - ?line stop_node(Node2), - ?line stop_node(Node1), + stop_node(Node2), + stop_node(Node1), ok. get_socket_priorities() -> @@ -622,185 +878,178 @@ get_socket_priorities() -> element(2, erlang:port_info(Port, name)) =:= "tcp_inet"]]. - + %% %% Testcase: %% monitor_nodes_nodedown_reason %% -monitor_nodes_nodedown_reason(doc) -> []; -monitor_nodes_nodedown_reason(suite) -> []; monitor_nodes_nodedown_reason(Config) when is_list(Config) -> - ?line MonNodeState = monitor_node_state(), - ?line ok = net_kernel:monitor_nodes(true), - ?line ok = net_kernel:monitor_nodes(true, [nodedown_reason]), - - ?line Names = get_numbered_nodenames(5, node), - ?line [NN1, NN2, NN3, NN4, NN5] = Names, - - ?line {ok, N1} = start_node(NN1), - ?line {ok, N2} = start_node(NN2), - ?line {ok, N3} = start_node(NN3), - ?line {ok, N4} = start_node(NN4, "-hidden"), - - ?line receive {nodeup, N1} -> ok end, - ?line receive {nodeup, N2} -> ok end, - ?line receive {nodeup, N3} -> ok end, - - ?line receive {nodeup, N1, []} -> ok end, - ?line receive {nodeup, N2, []} -> ok end, - ?line receive {nodeup, N3, []} -> ok end, - - ?line stop_node(N1), - ?line stop_node(N4), - ?line true = net_kernel:disconnect(N2), - ?line TickTime = net_kernel:get_net_ticktime(), - ?line SleepTime = TickTime + (TickTime div 4), - ?line spawn(N3, fun () -> - block_emu(SleepTime*1000), - halt() - end), - - ?line receive {nodedown, N1} -> ok end, - ?line receive {nodedown, N2} -> ok end, - ?line receive {nodedown, N3} -> ok end, - - ?line receive {nodedown, N1, [{nodedown_reason, R1}]} -> connection_closed = R1 end, - ?line receive {nodedown, N2, [{nodedown_reason, R2}]} -> disconnect = R2 end, - ?line receive {nodedown, N3, [{nodedown_reason, R3}]} -> net_tick_timeout = R3 end, - - ?line ok = net_kernel:monitor_nodes(false, [nodedown_reason]), - - ?line {ok, N5} = start_node(NN5), - ?line stop_node(N5), - - ?line receive {nodeup, N5} -> ok end, - ?line receive {nodedown, N5} -> ok end, - ?line print_my_messages(), - ?line ok = check_no_nodedown_nodeup(1000), - ?line ok = net_kernel:monitor_nodes(false), - ?line MonNodeState = monitor_node_state(), - ?line ok. - - -monitor_nodes_complex_nodedown_reason(doc) -> []; -monitor_nodes_complex_nodedown_reason(suite) -> []; + MonNodeState = monitor_node_state(), + ok = net_kernel:monitor_nodes(true), + ok = net_kernel:monitor_nodes(true, [nodedown_reason]), + + Names = get_numbered_nodenames(5, node), + [NN1, NN2, NN3, NN4, NN5] = Names, + + {ok, N1} = start_node(NN1), + {ok, N2} = start_node(NN2), + {ok, N3} = start_node(NN3), + {ok, N4} = start_node(NN4, "-hidden"), + + receive {nodeup, N1} -> ok end, + receive {nodeup, N2} -> ok end, + receive {nodeup, N3} -> ok end, + + receive {nodeup, N1, []} -> ok end, + receive {nodeup, N2, []} -> ok end, + receive {nodeup, N3, []} -> ok end, + + stop_node(N1), + stop_node(N4), + true = net_kernel:disconnect(N2), + TickTime = net_kernel:get_net_ticktime(), + SleepTime = TickTime + (TickTime div 2), + spawn(N3, fun () -> + block_emu(SleepTime*1000), + halt() + end), + + receive {nodedown, N1} -> ok end, + receive {nodedown, N2} -> ok end, + receive {nodedown, N3} -> ok end, + + receive {nodedown, N1, [{nodedown_reason, R1}]} -> connection_closed = R1 end, + receive {nodedown, N2, [{nodedown_reason, R2}]} -> disconnect = R2 end, + receive {nodedown, N3, [{nodedown_reason, R3}]} -> net_tick_timeout = R3 end, + + ok = net_kernel:monitor_nodes(false, [nodedown_reason]), + + {ok, N5} = start_node(NN5), + stop_node(N5), + + receive {nodeup, N5} -> ok end, + receive {nodedown, N5} -> ok end, + print_my_messages(), + ok = check_no_nodedown_nodeup(1000), + ok = net_kernel:monitor_nodes(false), + MonNodeState = monitor_node_state(), + ok. + + monitor_nodes_complex_nodedown_reason(Config) when is_list(Config) -> - ?line MonNodeState = monitor_node_state(), - ?line Me = self(), - ?line ok = net_kernel:monitor_nodes(true, [nodedown_reason]), - ?line [Name] = get_nodenames(1, monitor_nodes_complex_nodedown_reason), - ?line {ok, Node} = start_node(Name, ""), - ?line Pid = spawn(Node, - fun() -> - Me ! {stuff, - self(), - [make_ref(), - {processes(), erlang:ports()}]} - end), - ?line receive {nodeup, Node, []} -> ok end, - ?line {ok, NodeInfo} = net_kernel:node_info(Node), - ?line {value,{owner, Owner}} = lists:keysearch(owner, 1, NodeInfo), - ?line ComplexTerm = receive {stuff, Pid, _} = Msg -> - {Msg, term_to_binary(Msg)} - end, - ?line exit(Owner, ComplexTerm), - ?line receive - {nodedown, Node, [{nodedown_reason, NodeDownReason}]} -> - ?line ok - end, + MonNodeState = monitor_node_state(), + Me = self(), + ok = net_kernel:monitor_nodes(true, [nodedown_reason]), + [Name] = get_nodenames(1, monitor_nodes_complex_nodedown_reason), + {ok, Node} = start_node(Name, ""), + Pid = spawn(Node, + fun() -> + Me ! {stuff, + self(), + [make_ref(), + {processes(), erlang:ports()}]} + end), + receive {nodeup, Node, []} -> ok end, + {ok, NodeInfo} = net_kernel:node_info(Node), + {value,{owner, Owner}} = lists:keysearch(owner, 1, NodeInfo), + ComplexTerm = receive {stuff, Pid, _} = Msg -> + {Msg, term_to_binary(Msg)} + end, + exit(Owner, ComplexTerm), + receive + {nodedown, Node, [{nodedown_reason, NodeDownReason}]} -> + ok + end, %% If the complex nodedown_reason messed something up garbage collections %% are likely to dump core - ?line garbage_collect(), - ?line garbage_collect(), - ?line garbage_collect(), - ?line ComplexTerm = NodeDownReason, - ?line ok = net_kernel:monitor_nodes(false, [nodedown_reason]), - ?line no_msgs(), - ?line MonNodeState = monitor_node_state(), - ?line ok. - - - + garbage_collect(), + garbage_collect(), + garbage_collect(), + ComplexTerm = NodeDownReason, + ok = net_kernel:monitor_nodes(false, [nodedown_reason]), + no_msgs(), + MonNodeState = monitor_node_state(), + ok. + + + %% %% Testcase: %% monitor_nodes_node_type %% -monitor_nodes_node_type(doc) -> []; -monitor_nodes_node_type(suite) -> []; monitor_nodes_node_type(Config) when is_list(Config) -> - ?line MonNodeState = monitor_node_state(), - ?line ok = net_kernel:monitor_nodes(true), - ?line ok = net_kernel:monitor_nodes(true, [{node_type, all}]), - ?line Names = get_numbered_nodenames(9, node), -% ?line ?t:format("Names: ~p~n", [Names]), - ?line [NN1, NN2, NN3, NN4, NN5, NN6, NN7, NN8, NN9] = Names, - - ?line {ok, N1} = start_node(NN1), - ?line {ok, N2} = start_node(NN2), - ?line {ok, N3} = start_node(NN3, "-hidden"), - ?line {ok, N4} = start_node(NN4, "-hidden"), - - ?line receive {nodeup, N1} -> ok end, - ?line receive {nodeup, N2} -> ok end, - - ?line receive {nodeup, N1, [{node_type, visible}]} -> ok end, - ?line receive {nodeup, N2, [{node_type, visible}]} -> ok end, - ?line receive {nodeup, N3, [{node_type, hidden}]} -> ok end, - ?line receive {nodeup, N4, [{node_type, hidden}]} -> ok end, - - ?line stop_node(N1), - ?line stop_node(N2), - ?line stop_node(N3), - ?line stop_node(N4), - - ?line receive {nodedown, N1} -> ok end, - ?line receive {nodedown, N2} -> ok end, - - ?line receive {nodedown, N1, [{node_type, visible}]} -> ok end, - ?line receive {nodedown, N2, [{node_type, visible}]} -> ok end, - ?line receive {nodedown, N3, [{node_type, hidden}]} -> ok end, - ?line receive {nodedown, N4, [{node_type, hidden}]} -> ok end, - - ?line ok = net_kernel:monitor_nodes(false, [{node_type, all}]), - ?line {ok, N5} = start_node(NN5), - - ?line receive {nodeup, N5} -> ok end, - ?line stop_node(N5), - ?line receive {nodedown, N5} -> ok end, - - ?line ok = net_kernel:monitor_nodes(true, [{node_type, hidden}]), - ?line {ok, N6} = start_node(NN6), - ?line {ok, N7} = start_node(NN7, "-hidden"), - - - ?line receive {nodeup, N6} -> ok end, - ?line receive {nodeup, N7, [{node_type, hidden}]} -> ok end, - ?line stop_node(N6), - ?line stop_node(N7), - - ?line receive {nodedown, N6} -> ok end, - ?line receive {nodedown, N7, [{node_type, hidden}]} -> ok end, - - ?line ok = net_kernel:monitor_nodes(true, [{node_type, visible}]), - ?line ok = net_kernel:monitor_nodes(false, [{node_type, hidden}]), - ?line ok = net_kernel:monitor_nodes(false), - - ?line {ok, N8} = start_node(NN8), - ?line {ok, N9} = start_node(NN9, "-hidden"), - - ?line receive {nodeup, N8, [{node_type, visible}]} -> ok end, - ?line stop_node(N8), - ?line stop_node(N9), - - ?line receive {nodedown, N8, [{node_type, visible}]} -> ok end, - ?line print_my_messages(), - ?line ok = check_no_nodedown_nodeup(1000), - ?line ok = net_kernel:monitor_nodes(false, [{node_type, visible}]), - ?line MonNodeState = monitor_node_state(), - ?line ok. + MonNodeState = monitor_node_state(), + ok = net_kernel:monitor_nodes(true), + ok = net_kernel:monitor_nodes(true, [{node_type, all}]), + Names = get_numbered_nodenames(9, node), + [NN1, NN2, NN3, NN4, NN5, NN6, NN7, NN8, NN9] = Names, + + {ok, N1} = start_node(NN1), + {ok, N2} = start_node(NN2), + {ok, N3} = start_node(NN3, "-hidden"), + {ok, N4} = start_node(NN4, "-hidden"), + + receive {nodeup, N1} -> ok end, + receive {nodeup, N2} -> ok end, + + receive {nodeup, N1, [{node_type, visible}]} -> ok end, + receive {nodeup, N2, [{node_type, visible}]} -> ok end, + receive {nodeup, N3, [{node_type, hidden}]} -> ok end, + receive {nodeup, N4, [{node_type, hidden}]} -> ok end, + + stop_node(N1), + stop_node(N2), + stop_node(N3), + stop_node(N4), + + receive {nodedown, N1} -> ok end, + receive {nodedown, N2} -> ok end, + + receive {nodedown, N1, [{node_type, visible}]} -> ok end, + receive {nodedown, N2, [{node_type, visible}]} -> ok end, + receive {nodedown, N3, [{node_type, hidden}]} -> ok end, + receive {nodedown, N4, [{node_type, hidden}]} -> ok end, + + ok = net_kernel:monitor_nodes(false, [{node_type, all}]), + {ok, N5} = start_node(NN5), + + receive {nodeup, N5} -> ok end, + stop_node(N5), + receive {nodedown, N5} -> ok end, + + ok = net_kernel:monitor_nodes(true, [{node_type, hidden}]), + {ok, N6} = start_node(NN6), + {ok, N7} = start_node(NN7, "-hidden"), + + + receive {nodeup, N6} -> ok end, + receive {nodeup, N7, [{node_type, hidden}]} -> ok end, + stop_node(N6), + stop_node(N7), + + receive {nodedown, N6} -> ok end, + receive {nodedown, N7, [{node_type, hidden}]} -> ok end, + + ok = net_kernel:monitor_nodes(true, [{node_type, visible}]), + ok = net_kernel:monitor_nodes(false, [{node_type, hidden}]), + ok = net_kernel:monitor_nodes(false), + + {ok, N8} = start_node(NN8), + {ok, N9} = start_node(NN9, "-hidden"), + + receive {nodeup, N8, [{node_type, visible}]} -> ok end, + stop_node(N8), + stop_node(N9), + + receive {nodedown, N8, [{node_type, visible}]} -> ok end, + print_my_messages(), + ok = check_no_nodedown_nodeup(1000), + ok = net_kernel:monitor_nodes(false, [{node_type, visible}]), + MonNodeState = monitor_node_state(), + ok. %% @@ -808,95 +1057,89 @@ monitor_nodes_node_type(Config) when is_list(Config) -> %% monitor_nodes %% -monitor_nodes_misc(doc) -> []; -monitor_nodes_misc(suite) -> []; monitor_nodes_misc(Config) when is_list(Config) -> - ?line MonNodeState = monitor_node_state(), - ?line ok = net_kernel:monitor_nodes(true), - ?line ok = net_kernel:monitor_nodes(true, [{node_type, all}, nodedown_reason]), - ?line ok = net_kernel:monitor_nodes(true, [nodedown_reason, {node_type, all}]), - ?line Names = get_numbered_nodenames(3, node), -% ?line ?t:format("Names: ~p~n", [Names]), - ?line [NN1, NN2, NN3] = Names, - - ?line {ok, N1} = start_node(NN1), - ?line {ok, N2} = start_node(NN2, "-hidden"), - - ?line receive {nodeup, N1} -> ok end, - - ?line receive {nodeup, N1, [{node_type, visible}]} -> ok end, - ?line receive {nodeup, N1, [{node_type, visible}]} -> ok end, - ?line receive {nodeup, N2, [{node_type, hidden}]} -> ok end, - ?line receive {nodeup, N2, [{node_type, hidden}]} -> ok end, - - ?line stop_node(N1), - ?line stop_node(N2), - - ?line VisbleDownInfo = lists:sort([{node_type, visible}, - {nodedown_reason, connection_closed}]), - ?line HiddenDownInfo = lists:sort([{node_type, hidden}, - {nodedown_reason, connection_closed}]), - - ?line receive {nodedown, N1} -> ok end, - - ?line receive {nodedown, N1, Info1A} -> VisbleDownInfo = lists:sort(Info1A) end, - ?line receive {nodedown, N1, Info1B} -> VisbleDownInfo = lists:sort(Info1B) end, - ?line receive {nodedown, N2, Info2A} -> HiddenDownInfo = lists:sort(Info2A) end, - ?line receive {nodedown, N2, Info2B} -> HiddenDownInfo = lists:sort(Info2B) end, - - ?line ok = net_kernel:monitor_nodes(false, [{node_type, all}, nodedown_reason]), - - ?line {ok, N3} = start_node(NN3), - ?line receive {nodeup, N3} -> ok end, - ?line stop_node(N3), - ?line receive {nodedown, N3} -> ok end, - ?line print_my_messages(), - ?line ok = check_no_nodedown_nodeup(1000), - ?line ok = net_kernel:monitor_nodes(false), - ?line MonNodeState = monitor_node_state(), - ?line ok. - - -monitor_nodes_otp_6481(doc) -> - ["Tests that {nodeup, Node} messages are received before " - "messages from Node and that {nodedown, Node} messages are" - "received after messages from Node"]; -monitor_nodes_otp_6481(suite) -> - []; + MonNodeState = monitor_node_state(), + ok = net_kernel:monitor_nodes(true), + ok = net_kernel:monitor_nodes(true, [{node_type, all}, nodedown_reason]), + ok = net_kernel:monitor_nodes(true, [nodedown_reason, {node_type, all}]), + Names = get_numbered_nodenames(3, node), + [NN1, NN2, NN3] = Names, + + {ok, N1} = start_node(NN1), + {ok, N2} = start_node(NN2, "-hidden"), + + receive {nodeup, N1} -> ok end, + + receive {nodeup, N1, [{node_type, visible}]} -> ok end, + receive {nodeup, N1, [{node_type, visible}]} -> ok end, + receive {nodeup, N2, [{node_type, hidden}]} -> ok end, + receive {nodeup, N2, [{node_type, hidden}]} -> ok end, + + stop_node(N1), + stop_node(N2), + + VisbleDownInfo = lists:sort([{node_type, visible}, + {nodedown_reason, connection_closed}]), + HiddenDownInfo = lists:sort([{node_type, hidden}, + {nodedown_reason, connection_closed}]), + + receive {nodedown, N1} -> ok end, + + receive {nodedown, N1, Info1A} -> VisbleDownInfo = lists:sort(Info1A) end, + receive {nodedown, N1, Info1B} -> VisbleDownInfo = lists:sort(Info1B) end, + receive {nodedown, N2, Info2A} -> HiddenDownInfo = lists:sort(Info2A) end, + receive {nodedown, N2, Info2B} -> HiddenDownInfo = lists:sort(Info2B) end, + + ok = net_kernel:monitor_nodes(false, [{node_type, all}, nodedown_reason]), + + {ok, N3} = start_node(NN3), + receive {nodeup, N3} -> ok end, + stop_node(N3), + receive {nodedown, N3} -> ok end, + print_my_messages(), + ok = check_no_nodedown_nodeup(1000), + ok = net_kernel:monitor_nodes(false), + MonNodeState = monitor_node_state(), + ok. + + +%% Tests that {nodeup, Node} messages are received before +%% messages from Node and that {nodedown, Node} messages are +%% received after messages from Node. monitor_nodes_otp_6481(Config) when is_list(Config) -> - ?line ?t:format("Testing nodedown...~n"), - ?line monitor_nodes_otp_6481_test(Config, nodedown), - ?line ?t:format("ok~n"), - ?line ?t:format("Testing nodeup...~n"), - ?line monitor_nodes_otp_6481_test(Config, nodeup), - ?line ?t:format("ok~n"), - ?line ok. + io:format("Testing nodedown...~n"), + monitor_nodes_otp_6481_test(Config, nodedown), + io:format("ok~n"), + io:format("Testing nodeup...~n"), + monitor_nodes_otp_6481_test(Config, nodeup), + io:format("ok~n"), + ok. monitor_nodes_otp_6481_test(Config, TestType) when is_list(Config) -> - ?line MonNodeState = monitor_node_state(), - ?line NodeMsg = make_ref(), - ?line Me = self(), - ?line [Name] = get_nodenames(1, monitor_nodes_otp_6481), - ?line case TestType of - nodedown -> ?line ok = net_kernel:monitor_nodes(true); - nodeup -> ?line ok - end, - ?line Seq = lists:seq(1,10000), - ?line MN = spawn_link( - fun () -> - ?line lists:foreach( - fun (_) -> - ?line ok = net_kernel:monitor_nodes(true) - end, - Seq), - ?line Me ! {mon_set, self()}, - ?line receive after infinity -> ok end - end), - ?line receive {mon_set, MN} -> ok end, - ?line case TestType of - nodedown -> ?line ok; - nodeup -> ?line ok = net_kernel:monitor_nodes(true) - end, + MonNodeState = monitor_node_state(), + NodeMsg = make_ref(), + Me = self(), + [Name] = get_nodenames(1, monitor_nodes_otp_6481), + case TestType of + nodedown -> ok = net_kernel:monitor_nodes(true); + nodeup -> ok + end, + Seq = lists:seq(1,10000), + MN = spawn_link( + fun () -> + lists:foreach( + fun (_) -> + ok = net_kernel:monitor_nodes(true) + end, + Seq), + Me ! {mon_set, self()}, + receive after infinity -> ok end + end), + receive {mon_set, MN} -> ok end, + case TestType of + nodedown -> ok; + nodeup -> ok = net_kernel:monitor_nodes(true) + end, %% Whitebox: %% nodedown test: Since this process was the first one monitoring @@ -907,170 +1150,162 @@ monitor_nodes_otp_6481_test(Config, TestType) when is_list(Config) -> %% on nodeup %% Verify the monitor_nodes order expected - ?line TestMonNodeState = monitor_node_state(), - %?line ?t:format("~p~n", [TestMonNodeState]), - ?line TestMonNodeState = - MonNodeState - ++ case TestType of - nodedown -> [{self(), []}]; - nodeup -> [] - end + TestMonNodeState = monitor_node_state(), + %% io:format("~p~n", [TestMonNodeState]), + TestMonNodeState = + case TestType of + nodedown -> []; + nodeup -> [{self(), []}] + end ++ lists:map(fun (_) -> {MN, []} end, Seq) ++ case TestType of - nodedown -> []; - nodeup -> [{self(), []}] - end, - - - ?line {ok, Node} = start_node(Name, "", this), - ?line receive {nodeup, Node} -> ok end, - - ?line RemotePid = spawn(Node, - fun () -> - receive after 1500 -> ok end, - % infinit loop of msgs - % we want an endless stream of messages and the kill - % the node mercilessly. - % We then want to ensure that the nodedown message arrives - % last ... without garbage after it. - _ = spawn(fun() -> node_loop_send(Me, NodeMsg, 1) end), - receive {Me, kill_it} -> ok end, - halt() - end), + nodedown -> [{self(), []}]; + nodeup -> [] + end + ++ MonNodeState, + + {ok, Node} = start_node(Name, "", this), + receive {nodeup, Node} -> ok end, + + RemotePid = spawn(Node, + fun () -> + receive after 1500 -> ok end, + %% infinite loop of msgs + %% we want an endless stream of messages and the kill + %% the node mercilessly. + %% We then want to ensure that the nodedown message arrives + %% last ... without garbage after it. + _ = spawn(fun() -> node_loop_send(Me, NodeMsg, 1) end), + receive {Me, kill_it} -> ok end, + halt() + end), - ?line net_kernel:disconnect(Node), - ?line receive {nodedown, Node} -> ok end, + net_kernel:disconnect(Node), + receive {nodedown, Node} -> ok end, %% Verify that '{nodeup, Node}' comes before '{NodeMsg, 1}' (the message %% bringing up the connection). - ?line no_msgs(500), - ?line {nodeup, Node} = receive Msg1 -> Msg1 end, - ?line {NodeMsg, 1} = receive Msg2 -> Msg2 end, - % msg stream has begun, kill the node - ?line RemotePid ! {self(), kill_it}, + {nodeup, Node} = receive Msg1 -> Msg1 end, + {NodeMsg, N} = receive Msg2 -> Msg2 end, + %% msg stream has begun, kill the node + RemotePid ! {self(), kill_it}, %% Verify that '{nodedown, Node}' comes after the last '{NodeMsg, N}' %% message. - ?line {nodedown, Node} = flush_node_msgs(NodeMsg, 2), - ?line no_msgs(500), - - ?line Mon = erlang:monitor(process, MN), - ?line unlink(MN), - ?line exit(MN, bang), - ?line receive {'DOWN', Mon, process, MN, bang} -> ok end, - ?line ok = net_kernel:monitor_nodes(false), - ?line MonNodeState = monitor_node_state(), - ?line ok. + {nodedown, Node} = flush_node_msgs(NodeMsg, N+1), + no_msgs(500), + + Mon = erlang:monitor(process, MN), + unlink(MN), + exit(MN, bang), + receive {'DOWN', Mon, process, MN, bang} -> ok end, + ok = net_kernel:monitor_nodes(false), + MonNodeState = monitor_node_state(), + ok. flush_node_msgs(NodeMsg, No) -> case receive Msg -> Msg end of - {NodeMsg, No} -> flush_node_msgs(NodeMsg, No+1); - OtherMsg -> OtherMsg + {NodeMsg, N} when N >= No -> + flush_node_msgs(NodeMsg, N+1); + OtherMsg -> + OtherMsg end. node_loop_send(Pid, Msg, No) -> Pid ! {Msg, No}, node_loop_send(Pid, Msg, No + 1). -monitor_nodes_errors(doc) -> - []; -monitor_nodes_errors(suite) -> - []; monitor_nodes_errors(Config) when is_list(Config) -> - ?line MonNodeState = monitor_node_state(), - ?line error = net_kernel:monitor_nodes(asdf), - ?line {error, - {unknown_options, - [gurka]}} = net_kernel:monitor_nodes(true, - [gurka]), - ?line {error, - {options_not_a_list, - gurka}} = net_kernel:monitor_nodes(true, - gurka), - ?line {error, - {option_value_mismatch, - [{node_type,visible}, - {node_type,hidden}]}} + MonNodeState = monitor_node_state(), + error = net_kernel:monitor_nodes(asdf), + {error, + {unknown_options, + [gurka]}} = net_kernel:monitor_nodes(true, + [gurka]), + {error, + {options_not_a_list, + gurka}} = net_kernel:monitor_nodes(true, + gurka), + {error, + {option_value_mismatch, + [{node_type,visible}, + {node_type,hidden}]}} = net_kernel:monitor_nodes(true, [{node_type,hidden}, {node_type,visible}]), - ?line {error, - {option_value_mismatch, - [{node_type,visible}, - {node_type,all}]}} + {error, + {option_value_mismatch, + [{node_type,visible}, + {node_type,all}]}} = net_kernel:monitor_nodes(true, [{node_type,all}, {node_type,visible}]), - ?line {error, - {bad_option_value, - {node_type, - blaha}}} + {error, + {bad_option_value, + {node_type, + blaha}}} = net_kernel:monitor_nodes(true, [{node_type, blaha}]), - ?line MonNodeState = monitor_node_state(), - ?line ok. + MonNodeState = monitor_node_state(), + ok. -monitor_nodes_combinations(doc) -> - []; -monitor_nodes_combinations(suite) -> - []; monitor_nodes_combinations(Config) when is_list(Config) -> - ?line MonNodeState = monitor_node_state(), - ?line monitor_nodes_all_comb(true), - ?line [VisibleName, HiddenName] = get_nodenames(2, - monitor_nodes_combinations), - ?line {ok, Visible} = start_node(VisibleName, ""), - ?line receive_all_comb_nodeup_msgs(visible, Visible), - ?line no_msgs(), - ?line stop_node(Visible), - ?line receive_all_comb_nodedown_msgs(visible, Visible, connection_closed), - ?line no_msgs(), - ?line {ok, Hidden} = start_node(HiddenName, "-hidden"), - ?line receive_all_comb_nodeup_msgs(hidden, Hidden), - ?line no_msgs(), - ?line stop_node(Hidden), - ?line receive_all_comb_nodedown_msgs(hidden, Hidden, connection_closed), - ?line no_msgs(), - ?line monitor_nodes_all_comb(false), - ?line MonNodeState = monitor_node_state(), - ?line no_msgs(), - ?line ok. + MonNodeState = monitor_node_state(), + monitor_nodes_all_comb(true), + [VisibleName, HiddenName] = get_nodenames(2, + monitor_nodes_combinations), + {ok, Visible} = start_node(VisibleName, ""), + receive_all_comb_nodeup_msgs(visible, Visible), + no_msgs(), + stop_node(Visible), + receive_all_comb_nodedown_msgs(visible, Visible, connection_closed), + no_msgs(), + {ok, Hidden} = start_node(HiddenName, "-hidden"), + receive_all_comb_nodeup_msgs(hidden, Hidden), + no_msgs(), + stop_node(Hidden), + receive_all_comb_nodedown_msgs(hidden, Hidden, connection_closed), + no_msgs(), + monitor_nodes_all_comb(false), + MonNodeState = monitor_node_state(), + no_msgs(), + ok. monitor_nodes_all_comb(Flag) -> - ?line ok = net_kernel:monitor_nodes(Flag), - ?line ok = net_kernel:monitor_nodes(Flag, - [nodedown_reason]), - ?line ok = net_kernel:monitor_nodes(Flag, - [{node_type, hidden}]), - ?line ok = net_kernel:monitor_nodes(Flag, - [{node_type, visible}]), - ?line ok = net_kernel:monitor_nodes(Flag, - [{node_type, all}]), - ?line ok = net_kernel:monitor_nodes(Flag, - [nodedown_reason, - {node_type, hidden}]), - ?line ok = net_kernel:monitor_nodes(Flag, - [nodedown_reason, - {node_type, visible}]), - ?line ok = net_kernel:monitor_nodes(Flag, - [nodedown_reason, - {node_type, all}]), + ok = net_kernel:monitor_nodes(Flag), + ok = net_kernel:monitor_nodes(Flag, + [nodedown_reason]), + ok = net_kernel:monitor_nodes(Flag, + [{node_type, hidden}]), + ok = net_kernel:monitor_nodes(Flag, + [{node_type, visible}]), + ok = net_kernel:monitor_nodes(Flag, + [{node_type, all}]), + ok = net_kernel:monitor_nodes(Flag, + [nodedown_reason, + {node_type, hidden}]), + ok = net_kernel:monitor_nodes(Flag, + [nodedown_reason, + {node_type, visible}]), + ok = net_kernel:monitor_nodes(Flag, + [nodedown_reason, + {node_type, all}]), %% There currently are 8 different combinations - ?line 8. + 8. receive_all_comb_nodeup_msgs(visible, Node) -> - ?t:format("Receive nodeup visible...~n"), + io:format("Receive nodeup visible...~n"), Exp = [{nodeup, Node}, {nodeup, Node, []}] ++ mk_exp_mn_all_comb_nodeup_msgs_common(visible, Node), receive_mn_msgs(Exp), - ?t:format("ok~n"), + io:format("ok~n"), ok; receive_all_comb_nodeup_msgs(hidden, Node) -> - ?t:format("Receive nodeup hidden...~n"), + io:format("Receive nodeup hidden...~n"), Exp = mk_exp_mn_all_comb_nodeup_msgs_common(hidden, Node), receive_mn_msgs(Exp), - ?t:format("ok~n"), + io:format("ok~n"), ok. mk_exp_mn_all_comb_nodeup_msgs_common(Type, Node) -> @@ -1081,20 +1316,20 @@ mk_exp_mn_all_comb_nodeup_msgs_common(Type, Node) -> {nodeup, Node, InfoNt}]. receive_all_comb_nodedown_msgs(visible, Node, Reason) -> - ?t:format("Receive nodedown visible...~n"), + io:format("Receive nodedown visible...~n"), Exp = [{nodedown, Node}, {nodedown, Node, [{nodedown_reason, Reason}]}] ++ mk_exp_mn_all_comb_nodedown_msgs_common(visible, Node, Reason), receive_mn_msgs(Exp), - ?t:format("ok~n"), + io:format("ok~n"), ok; receive_all_comb_nodedown_msgs(hidden, Node, Reason) -> - ?t:format("Receive nodedown hidden...~n"), + io:format("Receive nodedown hidden...~n"), Exp = mk_exp_mn_all_comb_nodedown_msgs_common(hidden, Node, Reason), receive_mn_msgs(Exp), - ?t:format("ok~n"), + io:format("ok~n"), ok. mk_exp_mn_all_comb_nodedown_msgs_common(Type, Node, Reason) -> @@ -1108,81 +1343,73 @@ mk_exp_mn_all_comb_nodedown_msgs_common(Type, Node, Reason) -> receive_mn_msgs([]) -> ok; receive_mn_msgs(Msgs) -> - ?t:format("Expecting msgs: ~p~n", [Msgs]), + io:format("Expecting msgs: ~p~n", [Msgs]), receive {_Dir, _Node} = Msg -> - ?t:format("received ~p~n", [Msg]), + io:format("received ~p~n", [Msg]), case lists:member(Msg, Msgs) of true -> receive_mn_msgs(lists:delete(Msg, Msgs)); - false -> ?t:fail({unexpected_message, Msg, + false -> ct:fail({unexpected_message, Msg, expected_messages, Msgs}) end; {Dir, Node, Info} -> Msg = {Dir, Node, lists:sort(Info)}, - ?t:format("received ~p~n", [Msg]), + io:format("received ~p~n", [Msg]), case lists:member(Msg, Msgs) of true -> receive_mn_msgs(lists:delete(Msg, Msgs)); - false -> ?t:fail({unexpected_message, Msg, + false -> ct:fail({unexpected_message, Msg, expected_messages, Msgs}) end; Msg -> - ?t:format("received ~p~n", [Msg]), - ?t:fail({unexpected_message, Msg, + io:format("received ~p~n", [Msg]), + ct:fail({unexpected_message, Msg, expected_messages, Msgs}) end. -monitor_nodes_cleanup(doc) -> - []; -monitor_nodes_cleanup(suite) -> - []; monitor_nodes_cleanup(Config) when is_list(Config) -> - ?line MonNodeState = monitor_node_state(), - ?line Me = self(), - ?line No = monitor_nodes_all_comb(true), - ?line Inf = spawn(fun () -> - monitor_nodes_all_comb(true), - Me ! {mons_set, self()}, - receive after infinity -> ok end - end), - ?line TO = spawn(fun () -> - monitor_nodes_all_comb(true), - Me ! {mons_set, self()}, - receive after 500 -> ok end - end), - ?line receive {mons_set, Inf} -> ok end, - ?line receive {mons_set, TO} -> ok end, - ?line MNLen = length(MonNodeState) + No*3, - ?line MNLen = length(monitor_node_state()), - ?line MonInf = erlang:monitor(process, Inf), - ?line MonTO = erlang:monitor(process, TO), - ?line exit(Inf, bang), - ?line No = monitor_nodes_all_comb(false), - ?line receive {'DOWN', MonInf, process, Inf, bang} -> ok end, - ?line receive {'DOWN', MonTO, process, TO, normal} -> ok end, - ?line MonNodeState = monitor_node_state(), - ?line no_msgs(), - ?line ok. - -monitor_nodes_many(doc) -> - []; -monitor_nodes_many(suite) -> - []; + MonNodeState = monitor_node_state(), + Me = self(), + No = monitor_nodes_all_comb(true), + Inf = spawn(fun () -> + monitor_nodes_all_comb(true), + Me ! {mons_set, self()}, + receive after infinity -> ok end + end), + TO = spawn(fun () -> + monitor_nodes_all_comb(true), + Me ! {mons_set, self()}, + receive after 500 -> ok end + end), + receive {mons_set, Inf} -> ok end, + receive {mons_set, TO} -> ok end, + MNLen = length(MonNodeState) + No*3, + MNLen = length(monitor_node_state()), + MonInf = erlang:monitor(process, Inf), + MonTO = erlang:monitor(process, TO), + exit(Inf, bang), + No = monitor_nodes_all_comb(false), + receive {'DOWN', MonInf, process, Inf, bang} -> ok end, + receive {'DOWN', MonTO, process, TO, normal} -> ok end, + MonNodeState = monitor_node_state(), + no_msgs(), + ok. + monitor_nodes_many(Config) when is_list(Config) -> - ?line MonNodeState = monitor_node_state(), - ?line [Name] = get_nodenames(1, monitor_nodes_many), + MonNodeState = monitor_node_state(), + [Name] = get_nodenames(1, monitor_nodes_many), %% We want to perform more than 2^16 net_kernel:monitor_nodes %% since this will wrap an internal counter - ?line No = (1 bsl 16) + 17, - ?line repeat(fun () -> ok = net_kernel:monitor_nodes(true) end, No), - ?line No = length(monitor_node_state()) - length(MonNodeState), - ?line {ok, Node} = start_node(Name), - ?line repeat(fun () -> receive {nodeup, Node} -> ok end end, No), - ?line stop_node(Node), - ?line repeat(fun () -> receive {nodedown, Node} -> ok end end, No), - ?line ok = net_kernel:monitor_nodes(false), - ?line no_msgs(10), - ?line MonNodeState = monitor_node_state(), - ?line ok. + No = (1 bsl 16) + 17, + repeat(fun () -> ok = net_kernel:monitor_nodes(true) end, No), + No = length(monitor_node_state()) - length(MonNodeState), + {ok, Node} = start_node(Name), + repeat(fun () -> receive {nodeup, Node} -> ok end end, No), + stop_node(Node), + repeat(fun () -> receive {nodedown, Node} -> ok end end, No), + ok = net_kernel:monitor_nodes(false), + no_msgs(10), + MonNodeState = monitor_node_state(), + ok. %% Misc. functions @@ -1194,72 +1421,45 @@ monitor_node_state() -> check_no_nodedown_nodeup(TimeOut) -> - ?line receive - {nodeup, _, _} = Msg -> ?line ?t:fail({unexpected_nodeup, Msg}); - {nodeup, _} = Msg -> ?line ?t:fail({unexpected_nodeup, Msg}); - {nodedown, _, _} = Msg -> ?line ?t:fail({unexpected_nodedown, Msg}); - {nodedown, _} = Msg -> ?line ?t:fail({unexpected_nodedown, Msg}) - after TimeOut -> - ok - end. + receive + {nodeup, _, _} = Msg -> ct:fail({unexpected_nodeup, Msg}); + {nodeup, _} = Msg -> ct:fail({unexpected_nodeup, Msg}); + {nodedown, _, _} = Msg -> ct:fail({unexpected_nodedown, Msg}); + {nodedown, _} = Msg -> ct:fail({unexpected_nodedown, Msg}) + after TimeOut -> + ok + end. print_my_messages() -> - ?line {messages, Messages} = process_info(self(), messages), - ?line ?t:format("Messages: ~p~n", [Messages]), - ?line ok. - -%% Time difference in milliseconds !! -time_diff({TimeM, TimeS, TimeU}, {CurM, CurS, CurU}) when CurM > TimeM -> - ((CurM - TimeM) * 1000000000) + sec_diff({TimeS, TimeU}, {CurS, CurU}); -time_diff({_, TimeS, TimeU}, {_, CurS, CurU}) -> - sec_diff({TimeS, TimeU}, {CurS, CurU}). - -sec_diff({TimeS, TimeU}, {CurS, CurU}) when CurS > TimeS -> - ((CurS - TimeS) * 1000) + micro_diff(TimeU, CurU); -sec_diff({_, TimeU}, {_, CurU}) -> - micro_diff(TimeU, CurU). + {messages, Messages} = process_info(self(), messages), + io:format("Messages: ~p~n", [Messages]), + ok. -micro_diff(TimeU, CurU) -> - trunc(CurU/1000) - trunc(TimeU/1000). sleep(T) -> receive after T * 1000 -> ok end. start_node(Name, Param, this) -> NewParam = Param ++ " -pa " ++ filename:dirname(code:which(?MODULE)), - ?t:start_node(Name, peer, [{args, NewParam}, {erl, [this]}]); + test_server:start_node(Name, peer, [{args, NewParam}, {erl, [this]}]); start_node(Name, Param, "this") -> NewParam = Param ++ " -pa " ++ filename:dirname(code:which(?MODULE)), - ?t:start_node(Name, peer, [{args, NewParam}, {erl, [this]}]); + test_server:start_node(Name, peer, [{args, NewParam}, {erl, [this]}]); start_node(Name, Param, Rel) when is_atom(Rel) -> NewParam = Param ++ " -pa " ++ filename:dirname(code:which(?MODULE)), - ?t:start_node(Name, peer, [{args, NewParam}, {erl, [{release, atom_to_list(Rel)}]}]); + test_server:start_node(Name, peer, [{args, NewParam}, {erl, [{release, atom_to_list(Rel)}]}]); start_node(Name, Param, Rel) when is_list(Rel) -> NewParam = Param ++ " -pa " ++ filename:dirname(code:which(?MODULE)), - ?t:start_node(Name, peer, [{args, NewParam}, {erl, [{release, Rel}]}]). + test_server:start_node(Name, peer, [{args, NewParam}, {erl, [{release, Rel}]}]). start_node(Name, Param) -> NewParam = Param ++ " -pa " ++ filename:dirname(code:which(?MODULE)), - ?t:start_node(Name, slave, [{args, NewParam}]). -% M = list_to_atom(from($@, atom_to_list(node()))), -% slave:start_link(M, Name, Param). + test_server:start_node(Name, slave, [{args, NewParam}]). start_node(Name) -> start_node(Name, ""). stop_node(Node) -> - ?t:stop_node(Node). -% erlang:monitor_node(Node, true), -% rpc:cast(Node, init, stop, []), -% receive -% {nodedown, Node} -> -% ok -% after 10000 -> -% test_server:fail({stop_node, Node}) -% end. - -% from(H, [H | T]) -> T; -% from(H, [_ | T]) -> from(H, T); -% from(H, []) -> []. + test_server:stop_node(Node). get_nodenames(N, T) -> get_nodenames(N, T, []). @@ -1267,16 +1467,12 @@ get_nodenames(N, T) -> get_nodenames(0, _, Acc) -> Acc; get_nodenames(N, T, Acc) -> - {A, B, C} = now(), + U = erlang:unique_integer([positive]), get_nodenames(N-1, T, [list_to_atom(atom_to_list(T) ++ "-" - ++ atom_to_list(?MODULE) - ++ "-" - ++ integer_to_list(A) + ++ ?MODULE_STRING ++ "-" - ++ integer_to_list(B) - ++ "-" - ++ integer_to_list(C)) | Acc]). + ++ integer_to_list(U)) | Acc]). get_numbered_nodenames(N, T) -> get_numbered_nodenames(N, T, []). @@ -1284,16 +1480,12 @@ get_numbered_nodenames(N, T) -> get_numbered_nodenames(0, _, Acc) -> Acc; get_numbered_nodenames(N, T, Acc) -> - {A, B, C} = now(), + U = erlang:unique_integer([positive]), NL = [list_to_atom(atom_to_list(T) ++ integer_to_list(N) ++ "-" - ++ atom_to_list(?MODULE) - ++ "-" - ++ integer_to_list(A) - ++ "-" - ++ integer_to_list(B) + ++ ?MODULE_STRING ++ "-" - ++ integer_to_list(C)) | Acc], + ++ integer_to_list(U)) | Acc], get_numbered_nodenames(N-1, T, NL). wait_until(Fun) -> diff --git a/lib/kernel/test/erl_distribution_wb_SUITE.erl b/lib/kernel/test/erl_distribution_wb_SUITE.erl index 3b8b2d9150..8256444bdc 100644 --- a/lib/kernel/test/erl_distribution_wb_SUITE.erl +++ b/lib/kernel/test/erl_distribution_wb_SUITE.erl @@ -1,24 +1,25 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2011. All Rights Reserved. +%% Copyright Ericsson AB 1999-2017. 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% -module(erl_distribution_wb_SUITE). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -include_lib("kernel/include/inet.hrl"). -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, @@ -29,7 +30,7 @@ %% 1) %% -%% Connections are now always set up symetrically with respect to +%% Connections are now always set up symmetrically with respect to %% publication. If connecting node doesn't send DFLAG_PUBLISHED %% the other node wont send DFLAG_PUBLISHED. If the connecting %% node send DFLAG_PUBLISHED but the other node doesn't send @@ -55,11 +56,18 @@ -define(DFLAG_HIDDEN_ATOM_CACHE,16#40). -define(DFLAG_NEW_FUN_TAGS,16#80). -define(DFLAG_EXTENDED_PIDS_PORTS,16#100). +-define(DFLAG_UTF8_ATOMS, 16#10000). %% From R9 and forward extended references is compulsory %% From R10 and forward extended pids and ports are compulsory --define(COMPULSORY_DFLAGS, (?DFLAG_EXTENDED_REFERENCES bor ?DFLAG_EXTENDED_PIDS_PORTS)). +%% From R20 and forward UTF8 atoms are compulsory +%% From R21 and forward NEW_FUN_TAGS is compulsory (no more tuple fallback {fun, ...}) +-define(COMPULSORY_DFLAGS, (?DFLAG_EXTENDED_REFERENCES bor + ?DFLAG_EXTENDED_PIDS_PORTS bor + ?DFLAG_UTF8_ATOMS bor + ?DFLAG_NEW_FUN_TAGS)). +-define(PASS_THROUGH, $p). -define(shutdown(X), exit(X)). -define(int16(X), [((X) bsr 8) band 16#ff, (X) band 16#ff]). @@ -70,7 +78,7 @@ -define(i16(X1,X0), (?u16(X1,X0) - - (if (X1) > 127 -> 16#10000; true -> 0 end))). + (if (X1) > 127 -> 16#10000; true -> 0 end))). -define(u16(X1,X0), (((X1) bsl 8) bor (X0))). @@ -78,7 +86,9 @@ -define(u32(X3,X2,X1,X0), (((X3) bsl 24) bor ((X2) bsl 16) bor ((X1) bsl 8) bor (X0))). -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap,{minutes,1}}]. all() -> [whitebox, switch_options, missing_compulsory_dflags]. @@ -100,39 +110,33 @@ end_per_group(_GroupName, Config) -> init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) -> - Dog=?t:timetrap(?t:minutes(1)), - [{watchdog, Dog}|Config]. - -end_per_testcase(_Func, Config) -> - Dog=?config(watchdog, Config), - ?t:timetrap_cancel(Dog). - -switch_options(doc) -> - ["Tests switching of options for the tcp port, as this is done" - " when the distribution port is to be shortcut into the emulator." - " Maybe this should be in the inet test suite, but only the distribution" - " does such horrible things..."]; + Config. + +end_per_testcase(_Func, _Config) -> + ok. + +%% Tests switching of options for the tcp port, as this is done +%% when the distribution port is to be shortcut into the emulator. +%% Maybe this should be in the inet test suite, but only the distribution +%% does such horrible things... switch_options(Config) when is_list(Config) -> ok = test_switch_active(), ok = test_switch_active_partial() , ok = test_switch_active_and_packet(), ok. - -whitebox(doc) -> - ["Whitebox testing of distribution handshakes. Tests both BC with R5 and " - "the md5 version. Note that after R6B, this should be revised to " - "remove BC code."]; + +%% Whitebox testing of distribution handshakes. whitebox(Config) when is_list(Config) -> - ?line {ok, Node} = start_node(?MODULE,""), - ?line Cookie = erlang:get_cookie(), - ?line {_,Host} = split(node()), - ?line ok = pending_up_md5(Node, join(ccc,Host), Cookie), - ?line ok = simultaneous_md5(Node, join('A',Host), Cookie), - ?line ok = simultaneous_md5(Node, join(zzzzzzzzzzzzzz,Host), Cookie), - ?line stop_node(Node), + {ok, Node} = start_node(?MODULE,""), + Cookie = erlang:get_cookie(), + {_,Host} = split(node()), + ok = pending_up_md5(Node, join(ccc,Host), Cookie), + ok = simultaneous_md5(Node, join('A',Host), Cookie), + ok = simultaneous_md5(Node, join(zzzzzzzzzzzzzz,Host), Cookie), + stop_node(Node), ok. - + %% %% The actual tests %% @@ -142,56 +146,56 @@ whitebox(Config) when is_list(Config) -> %% test_switch_active() -> - ?line {Client, Server} = socket_pair(0, 4), - ?line ok = write_packets_32(Client, 1, 5), + {Client, Server} = socket_pair(0, 4), + ok = write_packets_32(Client, 1, 5), receive after 2000 -> ok end, - ?line ok = read_packets(Server, 1, 1), + ok = read_packets(Server, 1, 1), receive after 2000 -> ok end, - ?line ok = read_packets(Server, 2, 2), - ?line inet:setopts(Server, [{active, true}]), - ?line ok = receive_packets(Server, 3, 5), + ok = read_packets(Server, 2, 2), + inet:setopts(Server, [{active, true}]), + ok = receive_packets(Server, 3, 5), close_pair({Client, Server}), ok. - + test_switch_active_partial() -> - ?line {Client, Server} = socket_pair(0, 4), - ?line ok = write_packets_32(Client, 1, 2), - ?line ok = gen_tcp:send(Client,[?int32(4), [0,0,0]]), + {Client, Server} = socket_pair(0, 4), + ok = write_packets_32(Client, 1, 2), + ok = gen_tcp:send(Client,[?int32(4), [0,0,0]]), receive after 2000 -> ok end, - ?line ok = read_packets(Server, 1, 1), + ok = read_packets(Server, 1, 1), receive after 2000 -> ok end, - ?line ok = read_packets(Server, 2, 2), - ?line inet:setopts(Server, [{active, true}]), - ?line ok = gen_tcp:send(Client,[3]), - ?line ok = write_packets_32(Client, 4, 5), - ?line ok = receive_packets(Server, 3, 5), + ok = read_packets(Server, 2, 2), + inet:setopts(Server, [{active, true}]), + ok = gen_tcp:send(Client,[3]), + ok = write_packets_32(Client, 4, 5), + ok = receive_packets(Server, 3, 5), close_pair({Client, Server}), ok. - + do_test_switch_active_and_packet(SendBefore, SendAfter) -> - ?line {Client, Server} = socket_pair(0, 2), - ?line ok = write_packets_16(Client, 1, 2), - ?line ok = gen_tcp:send(Client,SendBefore), + {Client, Server} = socket_pair(0, 2), + ok = write_packets_16(Client, 1, 2), + ok = gen_tcp:send(Client,SendBefore), receive after 2000 -> ok end, - ?line ok = read_packets(Server, 1, 1), + ok = read_packets(Server, 1, 1), receive after 2000 -> ok end, - ?line ok = read_packets(Server, 2, 2), - ?line inet:setopts(Server, [{packet,4}, {active, true}]), - ?line ok = gen_tcp:send(Client,SendAfter), - ?line ok = write_packets_32(Client, 4, 5), - ?line ok = receive_packets(Server, 3, 5), + ok = read_packets(Server, 2, 2), + inet:setopts(Server, [{packet,4}, {active, true}]), + ok = gen_tcp:send(Client,SendAfter), + ok = write_packets_32(Client, 4, 5), + ok = receive_packets(Server, 3, 5), close_pair({Client, Server}), ok. test_switch_active_and_packet() -> - ?line ok = do_test_switch_active_and_packet([0],[0,0,4,0,0,0,3]), - ?line ok = do_test_switch_active_and_packet([0,0],[0,4,0,0,0,3]), - ?line ok = do_test_switch_active_and_packet([0,0,0],[4,0,0,0,3]), - ?line ok = do_test_switch_active_and_packet([0,0,0,4],[0,0,0,3]), - ?line ok = do_test_switch_active_and_packet([0,0,0,4,0],[0,0,3]), - ?line ok = do_test_switch_active_and_packet([0,0,0,4,0,0],[0,3]), - ?line ok = do_test_switch_active_and_packet([0,0,0,4,0,0,0],[3]), - ?line ok = do_test_switch_active_and_packet([0,0,0,4,0,0,0,3],[]), + ok = do_test_switch_active_and_packet([0],[0,0,4,0,0,0,3]), + ok = do_test_switch_active_and_packet([0,0],[0,4,0,0,0,3]), + ok = do_test_switch_active_and_packet([0,0,0],[4,0,0,0,3]), + ok = do_test_switch_active_and_packet([0,0,0,4],[0,0,0,3]), + ok = do_test_switch_active_and_packet([0,0,0,4,0],[0,0,3]), + ok = do_test_switch_active_and_packet([0,0,0,4,0,0],[0,3]), + ok = do_test_switch_active_and_packet([0,0,0,4,0,0,0],[3]), + ok = do_test_switch_active_and_packet([0,0,0,4,0,0,0,3],[]), ok. @@ -199,181 +203,180 @@ test_switch_active_and_packet() -> %% Handshake tests %% pending_up_md5(Node,OurName,Cookie) -> - ?line {NA,NB} = split(Node), - ?line {port,PortNo,_} = erl_epmd:port_please(NA,NB), - ?line {ok, SocketA} = gen_tcp:connect(atom_to_list(NB),PortNo, - [{active,false}, - {packet,2}]), - ?line send_name(SocketA,OurName,5), - ?line ok = recv_status(SocketA), - ?line {hidden,Node,5,HisChallengeA} = recv_challenge(SocketA), % See 1) - ?line OurChallengeA = gen_challenge(), - ?line OurDigestA = gen_digest(HisChallengeA, Cookie), - ?line send_challenge_reply(SocketA, OurChallengeA, OurDigestA), - ?line ok = recv_challenge_ack(SocketA, OurChallengeA, Cookie), - %%% - %%% OK, one connection is up, now lets be nasty and try another up: - %%% - %%% But wait for a while, the other node might not have done setnode - %%% just yet... - ?line receive after 1000 -> ok end, - ?line {ok, SocketB} = gen_tcp:connect(atom_to_list(NB),PortNo, - [{active,false}, - {packet,2}]), - ?line send_name(SocketB,OurName,5), - ?line alive = recv_status(SocketB), - ?line send_status(SocketB, true), - ?line gen_tcp:close(SocketA), - ?line {hidden,Node,5,HisChallengeB} = recv_challenge(SocketB), % See 1) - ?line OurChallengeB = gen_challenge(), - ?line OurDigestB = gen_digest(HisChallengeB, Cookie), - ?line send_challenge_reply(SocketB, OurChallengeB, OurDigestB), - ?line ok = recv_challenge_ack(SocketB, OurChallengeB, Cookie), - %%% - %%% Well, are we happy? - %%% - - ?line inet:setopts(SocketB, [{active, false}, - {packet, 4}]), - ?line gen_tcp:send(SocketB,build_rex_message('',OurName)), - ?line {Header, Message} = recv_message(SocketB), - ?line io:format("Received header ~p, data ~p.~n", + {NA,NB} = split(Node), + {port,PortNo,_} = erl_epmd:port_please(NA,NB), + {ok, SocketA} = gen_tcp:connect(atom_to_list(NB),PortNo, + [{active,false}, + {packet,2}]), + send_name(SocketA,OurName,5), + ok = recv_status(SocketA), + {hidden,Node,5,HisChallengeA} = recv_challenge(SocketA), % See 1) + OurChallengeA = gen_challenge(), + OurDigestA = gen_digest(HisChallengeA, Cookie), + send_challenge_reply(SocketA, OurChallengeA, OurDigestA), + ok = recv_challenge_ack(SocketA, OurChallengeA, Cookie), +%%% +%%% OK, one connection is up, now lets be nasty and try another up: +%%% +%%% But wait for a while, the other node might not have done setnode +%%% just yet... + receive after 1000 -> ok end, + {ok, SocketB} = gen_tcp:connect(atom_to_list(NB),PortNo, + [{active,false}, + {packet,2}]), + send_name(SocketB,OurName,5), + alive = recv_status(SocketB), + send_status(SocketB, true), + gen_tcp:close(SocketA), + {hidden,Node,5,HisChallengeB} = recv_challenge(SocketB), % See 1) + OurChallengeB = gen_challenge(), + OurDigestB = gen_digest(HisChallengeB, Cookie), + send_challenge_reply(SocketB, OurChallengeB, OurDigestB), + ok = recv_challenge_ack(SocketB, OurChallengeB, Cookie), +%%% +%%% Well, are we happy? +%%% + + inet:setopts(SocketB, [{active, false}, + {packet, 4}]), + gen_tcp:send(SocketB,build_rex_message('',OurName)), + {Header, Message} = recv_message(SocketB), + io:format("Received header ~p, data ~p.~n", [Header, Message]), - ?line gen_tcp:close(SocketB), + gen_tcp:close(SocketB), ok. simultaneous_md5(Node, OurName, Cookie) when OurName < Node -> - ?line pong = net_adm:ping(Node), - ?line LSocket = case gen_tcp:listen(0, [{active, false}, {packet,2}]) of + pong = net_adm:ping(Node), + LSocket = case gen_tcp:listen(0, [{active, false}, {packet,2}]) of {ok, Socket} -> Socket; Else -> exit(Else) end, - ?line EpmdSocket = register(OurName, LSocket, 1, 5), - ?line {NA, NB} = split(Node), - ?line rpc:cast(Node, net_adm, ping, [OurName]), - ?line receive after 1000 -> ok end, - ?line {port, PortNo, _} = erl_epmd:port_please(NA,NB), - ?line {ok, SocketA} = gen_tcp:connect(atom_to_list(NB),PortNo, - [{active,false}, - {packet,2}]), - ?line send_name(SocketA,OurName,5), + EpmdSocket = register(OurName, LSocket, 1, 5), + {NA, NB} = split(Node), + rpc:cast(Node, net_adm, ping, [OurName]), + receive after 1000 -> ok end, + {port, PortNo, _} = erl_epmd:port_please(NA,NB), + {ok, SocketA} = gen_tcp:connect(atom_to_list(NB),PortNo, + [{active,false}, + {packet,2}]), + send_name(SocketA,OurName,5), %% We are still not marked up on the other side, as our first message %% is not sent. - ?line SocketB = case gen_tcp:accept(LSocket) of + SocketB = case gen_tcp:accept(LSocket) of {ok, Socket1} -> - ?line Socket1; + Socket1; Else2 -> - ?line exit(Else2) + exit(Else2) end, - ?line nok = recv_status(SocketA), - % Now we are expected to close A - ?line gen_tcp:close(SocketA), - % But still Socket B will continue - ?line {normal,Node,5} = recv_name(SocketB), % See 1) - ?line send_status(SocketB, ok_simultaneous), - ?line MyChallengeB = gen_challenge(), - ?line send_challenge(SocketB, OurName, MyChallengeB,5), - ?line HisChallengeB = recv_challenge_reply(SocketB, MyChallengeB, Cookie), - ?line DigestB = gen_digest(HisChallengeB,Cookie), - ?line send_challenge_ack(SocketB, DigestB), - ?line inet:setopts(SocketB, [{active, false}, - {packet, 4}]), - % This should be the ping message. - ?line {Header, Message} = recv_message(SocketB), - ?line io:format("Received header ~p, data ~p.~n", + nok = recv_status(SocketA), + %% Now we are expected to close A + gen_tcp:close(SocketA), + %% But still Socket B will continue + {normal,Node,5} = recv_name(SocketB), % See 1) + send_status(SocketB, ok_simultaneous), + MyChallengeB = gen_challenge(), + send_challenge(SocketB, OurName, MyChallengeB,5), + HisChallengeB = recv_challenge_reply(SocketB, MyChallengeB, Cookie), + DigestB = gen_digest(HisChallengeB,Cookie), + send_challenge_ack(SocketB, DigestB), + inet:setopts(SocketB, [{active, false}, + {packet, 4}]), + %% This should be the ping message. + {Header, Message} = recv_message(SocketB), + io:format("Received header ~p, data ~p.~n", [Header, Message]), - ?line gen_tcp:close(SocketB), - ?line gen_tcp:close(LSocket), - ?line gen_tcp:close(EpmdSocket), + gen_tcp:close(SocketB), + gen_tcp:close(LSocket), + gen_tcp:close(EpmdSocket), ok; - + simultaneous_md5(Node, OurName, Cookie) when OurName > Node -> - ?line pong = net_adm:ping(Node), - ?line LSocket = case gen_tcp:listen(0, [{active, false}, {packet,2}]) of - {ok, Socket} -> - ?line Socket; - Else -> - ?line exit(Else) - end, - ?line EpmdSocket = register(OurName, LSocket, 1, 5), - ?line {NA, NB} = split(Node), - ?line rpc:cast(Node, net_adm, ping, [OurName]), - ?line receive after 1000 -> ok end, - ?line {port, PortNo, _} = erl_epmd:port_please(NA,NB), - ?line {ok, SocketA} = gen_tcp:connect(atom_to_list(NB),PortNo, - [{active,false}, - {packet,2}]), - ?line SocketB = case gen_tcp:accept(LSocket) of + pong = net_adm:ping(Node), + LSocket = case gen_tcp:listen(0, [{active, false}, {packet,2}]) of + {ok, Socket} -> + Socket; + Else -> + exit(Else) + end, + EpmdSocket = register(OurName, LSocket, 1, 5), + {NA, NB} = split(Node), + rpc:cast(Node, net_adm, ping, [OurName]), + receive after 1000 -> ok end, + {port, PortNo, _} = erl_epmd:port_please(NA,NB), + {ok, SocketA} = gen_tcp:connect(atom_to_list(NB),PortNo, + [{active,false}, + {packet,2}]), + SocketB = case gen_tcp:accept(LSocket) of {ok, Socket1} -> - ?line Socket1; + Socket1; Else2 -> - ?line exit(Else2) + exit(Else2) end, - ?line send_name(SocketA,OurName,5), - ?line ok_simultaneous = recv_status(SocketA), + send_name(SocketA,OurName,5), + ok_simultaneous = recv_status(SocketA), %% Socket B should die during this - ?line case catch begin - ?line {normal,Node,5} = recv_name(SocketB), % See 1) - ?line send_status(SocketB, ok_simultaneous), - ?line MyChallengeB = gen_challenge(), - ?line send_challenge(SocketB, OurName, MyChallengeB, - 5), - ?line HisChallengeB = recv_challenge_reply( - SocketB, - MyChallengeB, - Cookie), - ?line DigestB = gen_digest(HisChallengeB,Cookie), - ?line send_challenge_ack(SocketB, DigestB), - ?line inet:setopts(SocketB, [{active, false}, - {packet, 4}]), - ?line {HeaderB, MessageB} = recv_message(SocketB), - ?line io:format("Received header ~p, data ~p.~n", - [HeaderB, MessageB]) - end of - {'EXIT', Exitcode} -> - ?line io:format("Expected exitsignal caught: ~p.~n", - [Exitcode]); - Success -> - ?line io:format("Unexpected success: ~p~n", - [Success]), - ?line exit(unexpected_success) - end, - ?line gen_tcp:close(SocketB), + case catch begin + {normal,Node,5} = recv_name(SocketB), % See 1) + send_status(SocketB, ok_simultaneous), + MyChallengeB = gen_challenge(), + send_challenge(SocketB, OurName, MyChallengeB, + 5), + HisChallengeB = recv_challenge_reply( + SocketB, + MyChallengeB, + Cookie), + DigestB = gen_digest(HisChallengeB,Cookie), + send_challenge_ack(SocketB, DigestB), + inet:setopts(SocketB, [{active, false}, + {packet, 4}]), + {HeaderB, MessageB} = recv_message(SocketB), + io:format("Received header ~p, data ~p.~n", + [HeaderB, MessageB]) + end of + {'EXIT', Exitcode} -> + io:format("Expected exitsignal caught: ~p.~n", + [Exitcode]); + Success -> + io:format("Unexpected success: ~p~n", + [Success]), + exit(unexpected_success) + end, + gen_tcp:close(SocketB), %% But still Socket A will continue - ?line {hidden,Node,5,HisChallengeA} = recv_challenge(SocketA), % See 1) - ?line OurChallengeA = gen_challenge(), - ?line OurDigestA = gen_digest(HisChallengeA, Cookie), - ?line send_challenge_reply(SocketA, OurChallengeA, OurDigestA), - ?line ok = recv_challenge_ack(SocketA, OurChallengeA, Cookie), - - ?line inet:setopts(SocketA, [{active, false}, - {packet, 4}]), - ?line gen_tcp:send(SocketA,build_rex_message('',OurName)), - ?line {Header, Message} = recv_message(SocketA), - ?line io:format("Received header ~p, data ~p.~n", + {hidden,Node,5,HisChallengeA} = recv_challenge(SocketA), % See 1) + OurChallengeA = gen_challenge(), + OurDigestA = gen_digest(HisChallengeA, Cookie), + send_challenge_reply(SocketA, OurChallengeA, OurDigestA), + ok = recv_challenge_ack(SocketA, OurChallengeA, Cookie), + + inet:setopts(SocketA, [{active, false}, + {packet, 4}]), + gen_tcp:send(SocketA,build_rex_message('',OurName)), + {Header, Message} = recv_message(SocketA), + io:format("Received header ~p, data ~p.~n", [Header, Message]), - ?line gen_tcp:close(SocketA), - ?line gen_tcp:close(LSocket), - ?line gen_tcp:close(EpmdSocket), + gen_tcp:close(SocketA), + gen_tcp:close(LSocket), + gen_tcp:close(EpmdSocket), ok. -missing_compulsory_dflags(doc) -> []; missing_compulsory_dflags(Config) when is_list(Config) -> - ?line [Name1, Name2] = get_nodenames(2, missing_compulsory_dflags), - ?line {ok, Node} = start_node(Name1,""), - ?line {NA,NB} = split(Node), - ?line {port,PortNo,_} = erl_epmd:port_please(NA,NB), - ?line {ok, SocketA} = gen_tcp:connect(atom_to_list(NB),PortNo, - [{active,false}, - {packet,2}]), - ?line BadNode = list_to_atom(atom_to_list(Name2)++"@"++atom_to_list(NB)), - ?line send_name(SocketA,BadNode,5,0), - ?line not_allowed = recv_status(SocketA), - ?line gen_tcp:close(SocketA), - ?line stop_node(Node), - ?line ok. + [Name1, Name2] = get_nodenames(2, missing_compulsory_dflags), + {ok, Node} = start_node(Name1,""), + {NA,NB} = split(Node), + {port,PortNo,_} = erl_epmd:port_please(NA,NB), + {ok, SocketA} = gen_tcp:connect(atom_to_list(NB),PortNo, + [{active,false}, + {packet,2}]), + BadNode = list_to_atom(atom_to_list(Name2)++"@"++atom_to_list(NB)), + send_name(SocketA,BadNode,5,0), + not_allowed = recv_status(SocketA), + gen_tcp:close(SocketA), + stop_node(Node), + ok. %% %% Here comes the utilities @@ -436,7 +439,7 @@ socket_pair(ClientPack, ServerPack) -> {ok, Server} = gen_tcp:accept(Listen), gen_tcp:close(Listen), {Client, Server}. - + close_pair({Client, Server}) -> gen_tcp:close(Client), gen_tcp:close(Server), @@ -451,12 +454,9 @@ close_pair({Client, Server}) -> %% MD5 hashing %% -%% This is no proper random number, but that is not really important in -%% this test gen_challenge() -> - {_,_,N} = erlang:now(), - N. - + rand:uniform(1000000). + %% Generate a message digest from Challenge number and Cookie gen_digest(Challenge, Cookie) when is_integer(Challenge), is_atom(Cookie) -> C0 = erlang:md5_init(), @@ -597,15 +597,15 @@ do_register_node(NodeName, TcpPort, VLow, VHigh) -> Elen = length(Extra), Len = 1+2+1+1+2+2+2+length(Name)+2+Elen, gen_tcp:send(Socket, [?int16(Len), $x, - ?int16(TcpPort), - $M, - 0, - ?int16(VHigh), - ?int16(VLow), - ?int16(length(Name)), - Name, - ?int16(Elen), - Extra]), + ?int16(TcpPort), + $M, + 0, + ?int16(VHigh), + ?int16(VLow), + ?int16(length(Name)), + Name, + ?int16(Elen), + Extra]), case wait_for_reg_reply(Socket, []) of {error, epmd_close} -> exit(epmd_broken); @@ -668,24 +668,25 @@ split(Atom) -> build_rex_message(Cookie,OurName) -> [$?,term_to_binary({6,self(),Cookie,rex}), term_to_binary({'$gen_cast', - {cast, - rpc, - cast, - [OurName, hello, world, []], - self()} })]. + {cast, + rpc, + cast, + [OurName, hello, world, []], + self()} })]. %% Receive a distribution message recv_message(Socket) -> case gen_tcp:recv(Socket, 0) of + {ok,[]} -> + recv_message(Socket); %% a tick, ignore {ok,Data} -> B0 = list_to_binary(Data), - {_,B1} = erlang:split_binary(B0,1), - Header = binary_to_term(B1), - Siz = byte_size(term_to_binary(Header)), - {_,B2} = erlang:split_binary(B1,Siz), + <<?PASS_THROUGH, B1/binary>> = B0, + {Header,Siz} = binary_to_term(B1,[used]), + <<_:Siz/binary,B2/binary>> = B1, Message = case (catch binary_to_term(B2)) of {'EXIT', _} -> - could_not_digest_message; + {could_not_digest_message,B2}; Other -> Other end, @@ -700,10 +701,10 @@ join(Name,Host) -> %% start/stop slave. start_node(Name, Param) -> - ?t:start_node(Name, slave, [{args, Param}]). + test_server:start_node(Name, slave, [{args, Param}]). stop_node(Node) -> - ?t:stop_node(Node). + test_server:stop_node(Node). get_nodenames(N, T) -> @@ -712,13 +713,9 @@ get_nodenames(N, T) -> get_nodenames(0, _, Acc) -> Acc; get_nodenames(N, T, Acc) -> - {A, B, C} = now(), - get_nodenames(N-1, T, [list_to_atom(atom_to_list(?MODULE) + U = erlang:unique_integer([positive]), + get_nodenames(N-1, T, [list_to_atom(?MODULE_STRING ++ "-" ++ atom_to_list(T) ++ "-" - ++ integer_to_list(A) - ++ "-" - ++ integer_to_list(B) - ++ "-" - ++ integer_to_list(C)) | Acc]). + ++ integer_to_list(U)) | Acc]). diff --git a/lib/kernel/test/erl_prim_loader_SUITE.erl b/lib/kernel/test/erl_prim_loader_SUITE.erl index 658c31c14d..16a127aa3e 100644 --- a/lib/kernel/test/erl_prim_loader_SUITE.erl +++ b/lib/kernel/test/erl_prim_loader_SUITE.erl @@ -1,49 +1,55 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2014. All Rights Reserved. +%% Copyright Ericsson AB 1996-2018. 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/. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at %% -%% 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. +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% -module(erl_prim_loader_SUITE). -include_lib("kernel/include/file.hrl"). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, + init_per_testcase/2,end_per_testcase/2, init_per_group/2,end_per_group/2]). -export([get_path/1, set_path/1, get_file/1, normalize_and_backslash/1, inet_existing/1, inet_coming_up/1, inet_disconnects/1, multiple_slaves/1, file_requests/1, local_archive/1, remote_archive/1, - primary_archive/1, virtual_dir_in_archive/1]). + primary_archive/1, virtual_dir_in_archive/1, + get_modules/1]). --export([init_per_testcase/2, end_per_testcase/2]). +-define(PRIM_FILE, prim_file). %%----------------------------------------------------------------- %% Test suite for erl_prim_loader. (Most code is run during system start/stop.) %%----------------------------------------------------------------- -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap,{minutes,3}}]. all() -> [get_path, set_path, get_file, normalize_and_backslash, inet_existing, inet_coming_up, inet_disconnects, multiple_slaves, file_requests, local_archive, remote_archive, - primary_archive, virtual_dir_in_archive]. + primary_archive, virtual_dir_in_archive, + get_modules]. groups() -> []. @@ -61,53 +67,102 @@ end_per_group(_GroupName, Config) -> Config. -init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) -> - Dog=?t:timetrap(?t:minutes(3)), - [{watchdog, Dog}|Config]. +init_per_testcase(_Func, Config) -> + Config. -end_per_testcase(_Func, Config) -> - Dog=?config(watchdog, Config), - ?t:timetrap_cancel(Dog). +end_per_testcase(_Func, _Config) -> + ok. -get_path(doc) -> []; get_path(Config) when is_list(Config) -> - ?line case erl_prim_loader:get_path() of - {ok, Path} when is_list(Path) -> - ok; - _ -> - test_server:fail(get_path) - end, + case erl_prim_loader:get_path() of + {ok, Path} when is_list(Path) -> + ok; + _ -> + ct:fail(get_path) + end, ok. -set_path(doc) -> []; set_path(Config) when is_list(Config) -> - ?line {ok, Path} = erl_prim_loader:get_path(), - ?line ok = erl_prim_loader:set_path(Path), - ?line {ok, Path} = erl_prim_loader:get_path(), + {ok, Path} = erl_prim_loader:get_path(), + ok = erl_prim_loader:set_path(Path), + {ok, Path} = erl_prim_loader:get_path(), NewPath = Path ++ ["dummy_dir","/dummy_dir/dummy_dir"], - ?line ok = erl_prim_loader:set_path(NewPath), - ?line {ok, NewPath} = erl_prim_loader:get_path(), + ok = erl_prim_loader:set_path(NewPath), + {ok, NewPath} = erl_prim_loader:get_path(), - ?line ok = erl_prim_loader:set_path(Path), % Reset path. - ?line {ok, Path} = erl_prim_loader:get_path(), + ok = erl_prim_loader:set_path(Path), % Reset path. + {ok, Path} = erl_prim_loader:get_path(), - ?line {'EXIT',_} = (catch erl_prim_loader:set_path(not_a_list)), - ?line {ok, Path} = erl_prim_loader:get_path(), + {'EXIT',_} = (catch erl_prim_loader:set_path(not_a_list)), + {ok, Path} = erl_prim_loader:get_path(), ok. -get_file(doc) -> []; get_file(Config) when is_list(Config) -> - ?line case erl_prim_loader:get_file("lists" ++ code:objfile_extension()) of - {ok,Bin,File} when is_binary(Bin), is_list(File) -> - ok; - _ -> - test_server:fail(get_valid_file) - end, - ?line error = erl_prim_loader:get_file("duuuuuuummmy_file"), - ?line error = erl_prim_loader:get_file(duuuuuuummmy_file), - ?line error = erl_prim_loader:get_file({dummy}), + case erl_prim_loader:get_file("lists" ++ code:objfile_extension()) of + {ok,Bin,File} when is_binary(Bin), is_list(File) -> + ok; + _ -> + ct:fail(get_valid_file) + end, + error = erl_prim_loader:get_file("duuuuuuummmy_file"), + error = erl_prim_loader:get_file(duuuuuuummmy_file), + error = erl_prim_loader:get_file({dummy}), + ok. + +get_modules(Config) -> + case test_server:is_cover() of + false -> do_get_modules(Config); + true -> {skip,"Cover"} + end. + +do_get_modules(Config) -> + PrivDir = proplists:get_value(priv_dir, Config), + NotADir = atom_to_list(?FUNCTION_NAME) ++ "_not_a_dir", + ok = file:write_file(filename:join(PrivDir, NotADir), <<>>), + ok = file:set_cwd(PrivDir), + + MsGood = lists:sort([lists,gen_server,gb_trees,code_server]), + Ms = [certainly_not_existing|MsGood], + SuccExp = [begin + F = code:which(M), + {ok,Code} = file:read_file(F), + {M,{F,erlang:md5(Code)}} + end || M <- MsGood], + FailExp = [{certainly_not_existing,enoent}], + + io:format("SuccExp = ~p\n", [SuccExp]), + io:format("FailExp = ~p\n", [FailExp]), + + Path = code:get_path(), + Process = fun(_, F, Code) -> {ok,{F,erlang:md5(Code)}} end, + {ok,{SuccExp,FailExp}} = get_modules_sorted(Ms, Process, Path), + + %% Test that an 'enotdir' error can be handled. + {ok,{SuccExp,FailExp}} = get_modules_sorted(Ms, Process, [NotADir|Path]), + + Name = inet_get_modules, + {ok, Node, BootPid} = complete_start_node(Name), + ThisDir = filename:dirname(code:which(?MODULE)), + true = rpc:call(Node, code, add_patha, [ThisDir]), + _ = rpc:call(Node, code, ensure_loaded, [?MODULE]), + {ok,{InetSucc,FailExp}} = rpc:call(Node, erl_prim_loader, + get_modules, [Ms,Process,Path]), + SuccExp = lists:sort(InetSucc), + + stop_node(Node), + unlink(BootPid), + exit(BootPid, kill), + ok. +get_modules_sorted(Ms, Process, Path) -> + case erl_prim_loader:get_modules(Ms, Process, Path) of + {ok,{Succ,FailExp}} -> + {ok,{lists:sort(Succ),lists:sort(FailExp)}}; + Other -> + Other + end. + normalize_and_backslash(Config) -> %% Test OTP-11170 case os:type() of @@ -117,7 +172,7 @@ normalize_and_backslash(Config) -> test_normalize_and_backslash(Config) end. test_normalize_and_backslash(Config) -> - PrivDir = ?config(priv_dir,Config), + PrivDir = proplists:get_value(priv_dir,Config), Dir = filename:join(PrivDir,"\\"), File = filename:join(Dir,"file-OTP-11170"), ok = file:make_dir(Dir), @@ -128,41 +183,27 @@ test_normalize_and_backslash(Config) -> ok = file:del_dir(Dir), ok. -inet_existing(doc) -> ["Start a node using the 'inet' loading method, ", - "from an already started boot server."]; +%% Start a node using the 'inet' loading method, +%% from an already started boot server. inet_existing(Config) when is_list(Config) -> Name = erl_prim_test_inet_existing, - Host = host(), - Cookie = atom_to_list(erlang:get_cookie()), - IpStr = ip_str(Host), - LFlag = get_loader_flag(os:type()), - Args = LFlag ++ " -hosts " ++ IpStr ++ - " -setcookie " ++ Cookie, - {ok, BootPid} = erl_boot_server:start_link([Host]), - {ok, Node} = start_node(Name, Args), + BootPid = start_boot_server(), + Node = start_node_using_inet(Name), {ok,[["inet"]]} = rpc:call(Node, init, get_argument, [loader]), stop_node(Node), unlink(BootPid), exit(BootPid, kill), ok. -inet_coming_up(doc) -> ["Start a node using the 'inet' loading method, ", - "but start the boot server afterwards."]; +%% Start a node using the 'inet' loading method, +%% but start the boot server afterwards. inet_coming_up(Config) when is_list(Config) -> Name = erl_prim_test_inet_coming_up, - Cookie = atom_to_list(erlang:get_cookie()), - Host = host(), - IpStr = ip_str(Host), - LFlag = get_loader_flag(os:type()), - Args = LFlag ++ - " -hosts " ++ IpStr ++ - " -setcookie " ++ Cookie, - {ok, Node} = start_node(Name, Args, [{wait, false}]), + Node = start_node_using_inet(Name, [{wait,false}]), %% Wait a while, then start boot server, and wait for node to start. - test_server:sleep(test_server:seconds(6)), - io:format("erl_boot_server:start_link([~p]).", [Host]), - {ok, BootPid} = erl_boot_server:start_link([Host]), + ct:sleep({seconds,6}), + BootPid = start_boot_server(), wait_really_started(Node, 25), %% Check loader argument, then cleanup. @@ -173,41 +214,36 @@ inet_coming_up(Config) when is_list(Config) -> ok. wait_really_started(Node, 0) -> - test_server:fail({not_booted,Node}); + ct:fail({not_booted,Node}); wait_really_started(Node, N) -> case rpc:call(Node, init, get_status, []) of {started, _} -> ok; _ -> - test_server:sleep(1000), + ct:sleep(1000), wait_really_started(Node, N - 1) end. -inet_disconnects(doc) -> ["Start a node using the 'inet' loading method, ", - "then lose the connection."]; +%% Start a node using the 'inet' loading method, +%% then lose the connection. inet_disconnects(Config) when is_list(Config) -> case test_server:is_native(erl_boot_server) of true -> {skip,"erl_boot_server is native"}; false -> - ?line Name = erl_prim_test_inet_disconnects, - ?line Host = host(), - ?line Cookie = atom_to_list(erlang:get_cookie()), - ?line IpStr = ip_str(Host), - ?line LFlag = get_loader_flag(os:type()), - ?line Args = LFlag ++ " -hosts " ++ IpStr ++ - " -setcookie " ++ Cookie, - - ?line {ok, BootPid} = erl_boot_server:start([Host]), + Name = erl_prim_test_inet_disconnects, + + BootPid = start_boot_server(), + unlink(BootPid), Self = self(), %% This process shuts down the boot server during loading. - ?line Stopper = spawn_link(fun() -> stop_boot(BootPid, Self) end), - ?line receive - {Stopper,ready} -> ok - end, + Stopper = spawn_link(fun() -> stop_boot(BootPid, Self) end), + receive + {Stopper,ready} -> ok + end, %% Let the loading begin... - ?line {ok, Node} = start_node(Name, Args, [{wait, false}]), + Node = start_node_using_inet(Name, [{wait,false}]), %% When the stopper is ready, the slave node should be %% looking for a boot server again. @@ -215,18 +251,18 @@ inet_disconnects(Config) when is_list(Config) -> {Stopper,ok} -> ok; {Stopper,{error,Reason}} -> - ?line ?t:fail(Reason) + ct:fail(Reason) after 60000 -> - ?line ?t:fail(stopper_died) + ct:fail(stopper_died) end, %% Start new boot server to see that loading is continued. - ?line {ok, BootPid2} = erl_boot_server:start_link([Host]), - ?line wait_really_started(Node, 25), - ?line {ok,[["inet"]]} = rpc:call(Node, init, get_argument, [loader]), - ?line stop_node(Node), - ?line unlink(BootPid2), - ?line exit(BootPid2, kill), + BootPid2 = start_boot_server(), + wait_really_started(Node, 25), + {ok,[["inet"]]} = rpc:call(Node, init, get_argument, [loader]), + stop_node(Node), + unlink(BootPid2), + exit(BootPid2, kill), ok end. @@ -255,81 +291,72 @@ get_calls(Count, Pid) -> {error,{trace_msg_timeout,Count}} end. -multiple_slaves(doc) -> - ["Start nodes in parallell, all using the 'inet' loading method, ", - "verify that the boot server manages"]; +%% Start nodes in parallel, all using the 'inet' loading method; +%% verify that the boot server manages. multiple_slaves(Config) when is_list(Config) -> - case os:type() of - {ose,_} -> - {comment, "OSE: multiple nodes not supported"}; - _ -> - ?line Name = erl_prim_test_multiple_slaves, - ?line Host = host(), - ?line Cookie = atom_to_list(erlang:get_cookie()), - ?line IpStr = ip_str(Host), - ?line LFlag = get_loader_flag(os:type()), - ?line Args = LFlag ++ " -hosts " ++ IpStr ++ - " -setcookie " ++ Cookie, - - NoOfNodes = 10, % no of slave nodes to be started - - NamesAndNodes = - lists:map(fun(N) -> - NameN = atom_to_list(Name) ++ - integer_to_list(N), - NodeN = NameN ++ "@" ++ Host, - {list_to_atom(NameN),list_to_atom(NodeN)} - end, lists:seq(1, NoOfNodes)), - - ?line Nodes = start_multiple_nodes(NamesAndNodes, Args, []), - - %% "queue up" the nodes to wait for the boot server to respond - %% (note: test_server supervises each node start by accept() - %% on a socket, the timeout value for the accept has to be quite - %% long for this test to work). - ?line test_server:sleep(test_server:seconds(5)), - %% start the code loading circus! - ?line {ok,BootPid} = erl_boot_server:start_link([Host]), - %% give the nodes a chance to boot up before attempting to stop them - ?line test_server:sleep(test_server:seconds(10)), - - ?line wait_and_shutdown(lists:reverse(Nodes), 30), - - ?line unlink(BootPid), - ?line exit(BootPid, kill), - ok - end. + Name = erl_prim_test_multiple_slaves, + Host = host(), + IpStr = ip_str(Host), + Args = " -loader inet -hosts " ++ IpStr, + + NoOfNodes = 10, % no of slave nodes to be started + + NamesAndNodes = + lists:map(fun(N) -> + NameN = atom_to_list(Name) ++ + integer_to_list(N), + NodeN = NameN ++ "@" ++ Host, + {list_to_atom(NameN),list_to_atom(NodeN)} + end, lists:seq(1, NoOfNodes)), + + Nodes = start_multiple_nodes(NamesAndNodes, Args, []), + + %% "queue up" the nodes to wait for the boot server to respond + %% (note: test_server supervises each node start by accept() + %% on a socket, the timeout value for the accept has to be quite + %% long for this test to work). + ct:sleep({seconds,5}), + %% start the code loading circus! + BootPid = start_boot_server(), + %% give the nodes a chance to boot up before attempting to stop them + ct:sleep({seconds,10}), + + wait_and_shutdown(lists:reverse(Nodes), 30), + + unlink(BootPid), + exit(BootPid, kill), + ok. start_multiple_nodes([{Name,Node} | NNs], Args, Started) -> - ?line {ok,Node} = start_node(Name, Args, [{wait, false}]), + {ok,Node} = start_node(Name, Args, [{wait, false}]), start_multiple_nodes(NNs, Args, [Node | Started]); start_multiple_nodes([], _, Nodes) -> Nodes. wait_and_shutdown([Node | Nodes], Tries) -> - ?line wait_really_started(Node, Tries), - ?line {ok,[["inet"]]} = rpc:call(Node, init, get_argument, [loader]), - ?line stop_node(Node), + wait_really_started(Node, Tries), + {ok,[["inet"]]} = rpc:call(Node, init, get_argument, [loader]), + stop_node(Node), wait_and_shutdown(Nodes, Tries); wait_and_shutdown([], _) -> ok. -file_requests(doc) -> ["Start a node using the 'inet' loading method, ", - "verify that the boot server responds to file requests."]; +%% Start a node using the 'inet' loading method, +%% verify that the boot server responds to file requests. file_requests(Config) when is_list(Config) -> - ?line {ok, Node, BootPid} = complete_start_node(erl_prim_test_file_req), + {ok, Node, BootPid} = complete_start_node(erl_prim_test_file_req), %% compare with results from file server calls (the %% boot server uses the same file sys and cwd) {ok,Files} = file:list_dir("."), io:format("Files: ~p~n",[Files]), - ?line {ok,Files} = rpc:call(Node, erl_prim_loader, list_dir, ["."]), + {ok,Files} = rpc:call(Node, erl_prim_loader, list_dir, ["."]), {ok,Info} = file:read_file_info(code:which(test_server)), - ?line {ok,Info} = rpc:call(Node, erl_prim_loader, read_file_info, - [code:which(test_server)]), + {ok,Info} = rpc:call(Node, erl_prim_loader, read_file_info, + [code:which(test_server)]), - PrivDir = ?config(priv_dir,Config), + PrivDir = proplists:get_value(priv_dir,Config), Dir = filename:join(PrivDir,?MODULE_STRING++"_file_requests"), ok = file:make_dir(Dir), Alias = filename:join(Dir,"symlink"), @@ -353,163 +380,140 @@ file_requests(Config) when is_list(Config) -> end, {ok,Cwd} = file:get_cwd(), - ?line {ok,Cwd} = rpc:call(Node, erl_prim_loader, get_cwd, []), + {ok,Cwd} = rpc:call(Node, erl_prim_loader, get_cwd, []), case file:get_cwd("C:") of {error,enotsup} -> ok; {ok,DCwd} -> - ?line {ok,DCwd} = rpc:call(Node, erl_prim_loader, get_cwd, ["C:"]) + {ok,DCwd} = rpc:call(Node, erl_prim_loader, get_cwd, ["C:"]) end, - ?line stop_node(Node), - ?line unlink(BootPid), - ?line exit(BootPid, kill), + stop_node(Node), + unlink(BootPid), + exit(BootPid, kill), ok. -complete_start_node(Name) -> - ?line Host = host(), - ?line Cookie = atom_to_list(erlang:get_cookie()), - ?line IpStr = ip_str(Host), - ?line LFlag = get_loader_flag(os:type()), - ?line Args = LFlag ++ " -hosts " ++ IpStr ++ - " -setcookie " ++ Cookie, - - ?line {ok,BootPid} = erl_boot_server:start_link([Host]), - - ?line {ok,Node} = start_node(Name, Args), - ?line wait_really_started(Node, 25), - {ok, Node, BootPid}. - -local_archive(suite) -> - []; -local_archive(doc) -> - ["Read files from local archive."]; +%% Read files from local archive. local_archive(Config) when is_list(Config) -> - PrivDir = ?config(priv_dir, Config), + PrivDir = proplists:get_value(priv_dir, Config), KernelDir = filename:basename(code:lib_dir(kernel)), Archive = filename:join([PrivDir, KernelDir ++ init:archive_extension()]), file:delete(Archive), - ?line {ok, Archive} = create_archive(Archive, [KernelDir]), + {ok, Archive} = create_archive(Archive, [KernelDir]), Node = node(), BeamName = "inet.beam", - ?line ok = test_archive(Node, Archive, KernelDir, BeamName), + ok = test_archive(Node, Archive, KernelDir, BeamName), %% Cleanup - ?line ok = rpc:call(Node, erl_prim_loader, release_archives, []), - ?line ok = file:delete(Archive), + ok = rpc:call(Node, erl_prim_loader, purge_archive_cache, []), + ok = file:delete(Archive), ok. -remote_archive(suite) -> - {req, [{local_slave_nodes, 1}, {time, 10}]}; -remote_archive(doc) -> - ["Read files from remote archive."]; +%% Read files from remote archive. remote_archive(Config) when is_list(Config) -> - PrivDir = ?config(priv_dir, Config), + PrivDir = proplists:get_value(priv_dir, Config), KernelDir = filename:basename(code:lib_dir(kernel)), Archive = filename:join([PrivDir, KernelDir ++ init:archive_extension()]), file:delete(Archive), - ?line {ok, Archive} = create_archive(Archive, [KernelDir]), + {ok, Archive} = create_archive(Archive, [KernelDir]), - ?line {ok, Node, BootPid} = complete_start_node(remote_archive), + {ok, Node, BootPid} = complete_start_node(remote_archive), BeamName = "inet.beam", - ?line ok = test_archive(Node, Archive, KernelDir, BeamName), + ok = test_archive(Node, Archive, KernelDir, BeamName), %% Cleanup - ?line stop_node(Node), - ?line unlink(BootPid), - ?line exit(BootPid, kill), + stop_node(Node), + unlink(BootPid), + exit(BootPid, kill), ok. -primary_archive(suite) -> - {req, [{local_slave_nodes, 1}, {time, 10}]}; -primary_archive(doc) -> - ["Read files from primary archive."]; +%% Read files from primary archive. primary_archive(Config) when is_list(Config) -> %% Copy the orig files to priv_dir - PrivDir = ?config(priv_dir, Config), + PrivDir = proplists:get_value(priv_dir, Config), Archive = filename:join([PrivDir, "primary_archive.zip"]), file:delete(Archive), - DataDir = ?config(data_dir, Config), - ?line {ok, _} = zip:create(Archive, ["primary_archive"], - [{compress, []}, {cwd, DataDir}]), - ?line {ok, _} = zip:extract(Archive, [{cwd, PrivDir}]), + DataDir = proplists:get_value(data_dir, Config), + {ok, _} = zip:create(Archive, ["primary_archive"], + [{compress, []}, {cwd, DataDir}]), + {ok, _} = zip:extract(Archive, [{cwd, PrivDir}]), TopDir = filename:join([PrivDir, "primary_archive"]), %% Compile the code DictDir = "primary_archive_dict-1.0", DummyDir = "primary_archive_dummy", - ?line ok = compile_app(TopDir, DictDir), - ?line ok = compile_app(TopDir, DummyDir), - + ok = compile_app(TopDir, DictDir), + ok = compile_app(TopDir, DummyDir), + %% Create the archive {ok, TopFiles} = file:list_dir(TopDir), - ?line {ok, {_, ArchiveBin}} = zip:create(Archive, TopFiles, - [memory, {compress, []}, {cwd, TopDir}]), - + {ok, {_, ArchiveBin}} = zip:create(Archive, TopFiles, + [memory, {compress, []}, {cwd, TopDir}]), + %% Use temporary node to simplify cleanup - ?line Cookie = atom_to_list(erlang:get_cookie()), - ?line Args = " -setcookie " ++ Cookie, - ?line {ok,Node} = start_node(primary_archive, Args), - ?line wait_really_started(Node, 25), - ?line {_,_,_} = rpc:call(Node, erlang, date, []), + Cookie = atom_to_list(erlang:get_cookie()), + Args = " -setcookie " ++ Cookie, + {ok,Node} = start_node(primary_archive, Args), + wait_really_started(Node, 25), + {_,_,_} = rpc:call(Node, erlang, date, []), %% Set primary archive ExpectedEbins = [Archive, DictDir ++ "/ebin", DummyDir ++ "/ebin"], io:format("ExpectedEbins: ~p\n", [ExpectedEbins]), - ?line {ok, FileInfo} = prim_file:read_file_info(Archive), - ?line {ok, Ebins} = rpc:call(Node, erl_prim_loader, set_primary_archive, - [Archive, ArchiveBin, FileInfo, - fun escript:parse_file/1]), - ?line ExpectedEbins = lists:sort(Ebins), % assert - - ?line {ok, TopFiles2} = rpc:call(Node, erl_prim_loader, list_dir, [Archive]), - ?line [DictDir, DummyDir] = lists:sort(TopFiles2), + {ok, FileInfo} = ?PRIM_FILE:read_file_info(Archive), + {ok, Ebins} = rpc:call(Node, erl_prim_loader, set_primary_archive, + [Archive, ArchiveBin, FileInfo, + fun escript:parse_file/1]), + ExpectedEbins = lists:sort(Ebins), % assert + + {ok, TopFiles2} = rpc:call(Node, erl_prim_loader, list_dir, [Archive]), + [DictDir, DummyDir] = lists:sort(TopFiles2), BeamName = "primary_archive_dict_app.beam", - ?line ok = test_archive(Node, Archive, DictDir, BeamName), - + ok = test_archive(Node, Archive, DictDir, BeamName), + %% Cleanup - ?line {ok, []} = rpc:call(Node, erl_prim_loader, set_primary_archive, - [undefined, undefined, undefined, - fun escript:parse_file/1]), - ?line stop_node(Node), - ?line ok = file:delete(Archive), + {ok, []} = rpc:call(Node, erl_prim_loader, set_primary_archive, + [undefined, undefined, undefined, + fun escript:parse_file/1]), + stop_node(Node), + ok = file:delete(Archive), ok. test_archive(Node, TopDir, AppDir, BeamName) -> %% List dir io:format("test_archive: ~p\n", [rpc:call(Node, erl_prim_loader, list_dir, [TopDir])]), - ?line {ok, TopFiles} = rpc:call(Node, erl_prim_loader, list_dir, [TopDir]), - ?line true = lists:member(AppDir, TopFiles), + {ok, TopFiles} = rpc:call(Node, erl_prim_loader, list_dir, [TopDir]), + true = lists:member(AppDir, TopFiles), AbsAppDir = TopDir ++ "/" ++ AppDir, - ?line {ok, AppFiles} = rpc:call(Node, erl_prim_loader, list_dir, [AbsAppDir]), - ?line true = lists:member("ebin", AppFiles), + {ok, AppFiles} = rpc:call(Node, erl_prim_loader, list_dir, [AbsAppDir]), + true = lists:member("ebin", AppFiles), Ebin = AbsAppDir ++ "/ebin", - ?line {ok, EbinFiles} = rpc:call(Node, erl_prim_loader, list_dir, [Ebin]), + {ok, EbinFiles} = rpc:call(Node, erl_prim_loader, list_dir, [Ebin]), Beam = Ebin ++ "/" ++ BeamName, - ?line true = lists:member(BeamName, EbinFiles), - ?line error = rpc:call(Node, erl_prim_loader, list_dir, [TopDir ++ "/no_such_file"]), - ?line error = rpc:call(Node, erl_prim_loader, list_dir, [TopDir ++ "/ebin/no_such_file"]), - + true = lists:member(BeamName, EbinFiles), + error = rpc:call(Node, erl_prim_loader, list_dir, [TopDir ++ "/no_such_file"]), + error = rpc:call(Node, erl_prim_loader, list_dir, [TopDir ++ "/ebin/no_such_file"]), + %% File info - ?line {ok, #file_info{type = directory}} = + {ok, #file_info{type = directory}} = rpc:call(Node, erl_prim_loader, read_file_info, [TopDir]), - ?line {ok, #file_info{type = directory}} = + {ok, #file_info{type = directory}} = rpc:call(Node, erl_prim_loader, read_file_info, [Ebin]), - ?line {ok, #file_info{type = regular} = FI} = + {ok, #file_info{type = regular} = FI} = rpc:call(Node, erl_prim_loader, read_file_info, [Beam]), - ?line error = rpc:call(Node, erl_prim_loader, read_file_info, [TopDir ++ "/no_such_file"]), - ?line error = rpc:call(Node, erl_prim_loader, read_file_info, [TopDir ++ "/ebin/no_such_file"]), - + error = rpc:call(Node, erl_prim_loader, read_file_info, [TopDir ++ "/no_such_file"]), + error = rpc:call(Node, erl_prim_loader, read_file_info, [TopDir ++ "/ebin/no_such_file"]), + %% Get file - ?line {ok, Bin, Beam} = rpc:call(Node, erl_prim_loader, get_file, [Beam]), - ?line if - FI#file_info.size =:= byte_size(Bin) -> ok; - true -> exit({FI#file_info.size, byte_size(Bin)}) - end, - ?line error = rpc:call(Node, erl_prim_loader, get_file, ["/no_such_file"]), - ?line error = rpc:call(Node, erl_prim_loader, get_file, ["/ebin/no_such_file"]), + {ok, Bin, Beam} = rpc:call(Node, erl_prim_loader, get_file, [Beam]), + if + FI#file_info.size =:= byte_size(Bin) -> ok; + true -> exit({FI#file_info.size, byte_size(Bin)}) + end, + error = rpc:call(Node, erl_prim_loader, get_file, ["/no_such_file"]), + error = rpc:call(Node, erl_prim_loader, get_file, ["/ebin/no_such_file"]), ok. create_archive(Archive, AppDirs) -> @@ -519,12 +523,9 @@ create_archive(Archive, AppDirs) -> zip:create(Archive, AppDirs, Opts). -virtual_dir_in_archive(suite) -> - []; -virtual_dir_in_archive(doc) -> - ["Read virtual directories from archive."]; +%% Read virtual directories from archive. virtual_dir_in_archive(Config) when is_list(Config) -> - PrivDir = ?config(priv_dir, Config), + PrivDir = proplists:get_value(priv_dir, Config), Data = <<"A little piece of data.">>, ArchiveBase = "archive_with_virtual_dirs", Archive = filename:join([PrivDir, ArchiveBase ++ init:archive_extension()]), @@ -533,32 +534,62 @@ virtual_dir_in_archive(Config) when is_list(Config) -> FileInArchive = filename:join([ArchiveBase, EbinBase, FileBase]), BinFiles = [{FileInArchive, Data}], Opts = [{compress, []}], - ?line file:delete(Archive), + file:delete(Archive), io:format("zip:create(~p,\n\t~p,\n\t~p).\n", [Archive, BinFiles, Opts]), - ?line {ok, Archive} = zip:create(Archive, BinFiles, Opts), + {ok, Archive} = zip:create(Archive, BinFiles, Opts), %% Verify that there is no directories - ?line {ok, BinFiles} = zip:unzip(Archive, [memory]), + {ok, BinFiles} = zip:unzip(Archive, [memory]), FullPath = filename:join([Archive, FileInArchive]), - ?line {ok, _} = erl_prim_loader:read_file_info(FullPath), + {ok, _} = erl_prim_loader:read_file_info(FullPath), %% Read one virtual dir EbinDir = filename:dirname(FullPath), - ?line {ok, _} = erl_prim_loader:read_file_info(EbinDir), - ?line {ok, [FileBase]} = erl_prim_loader:list_dir(EbinDir), + {ok, _} = erl_prim_loader:read_file_info(EbinDir), + {ok, [FileBase]} = erl_prim_loader:list_dir(EbinDir), %% Read another virtual dir AppDir = filename:dirname(EbinDir), - ?line {ok, _} = erl_prim_loader:read_file_info(AppDir), - ?line {ok, [EbinBase]} = erl_prim_loader:list_dir(AppDir), - + {ok, _} = erl_prim_loader:read_file_info(AppDir), + {ok, [EbinBase]} = erl_prim_loader:list_dir(AppDir), + %% Cleanup - ?line ok = erl_prim_loader:release_archives(), - ?line ok = file:delete(Archive), + ok = erl_prim_loader:purge_archive_cache(), + ok = file:delete(Archive), ok. -%% Misc. functions +%%% +%%% Helper functions. +%%% + +complete_start_node(Name) -> + BootPid = start_boot_server(), + Node = start_node_using_inet(Name), + wait_really_started(Node, 25), + {ok, Node, BootPid}. + +start_boot_server() -> + %% Many linux systems define: + %% 127.0.0.1 localhost + %% 127.0.1.1 somehostname + %% Therefore, to allow the tests to work on those kind of systems, + %% also include "localhost" in the list of allowed hosts. + + Hosts = [host(),ip_str("localhost")], + {ok,BootPid} = erl_boot_server:start_link(Hosts), + BootPid. + +start_node_using_inet(Name) -> + start_node_using_inet(Name, []). + +start_node_using_inet(Name, Opts) -> + Host = host(), + IpStr = ip_str(Host), + Args = " -loader inet -hosts " ++ IpStr, + {ok,Node} = start_node(Name, Args, Opts), + Node. + ip_str({A, B, C, D}) -> lists:concat([A, ".", B, ".", C, ".", D]); @@ -584,14 +615,11 @@ host() -> stop_node(Node) -> test_server:stop_node(Node). -get_loader_flag(_) -> - " -loader inet ". - compile_app(TopDir, AppName) -> AppDir = filename:join([TopDir, AppName]), SrcDir = filename:join([AppDir, "src"]), OutDir = filename:join([AppDir, "ebin"]), - ?line {ok, Files} = file:list_dir(SrcDir), + {ok, Files} = file:list_dir(SrcDir), compile_files(Files, SrcDir, OutDir). compile_files([File | Files], SrcDir, OutDir) -> diff --git a/lib/kernel/test/erl_prim_loader_SUITE_data/primary_archive/primary_archive_dict-1.0/src/primary_archive_dict.erl b/lib/kernel/test/erl_prim_loader_SUITE_data/primary_archive/primary_archive_dict-1.0/src/primary_archive_dict.erl index 2444224810..57aeb7fd64 100644 --- a/lib/kernel/test/erl_prim_loader_SUITE_data/primary_archive/primary_archive_dict-1.0/src/primary_archive_dict.erl +++ b/lib/kernel/test/erl_prim_loader_SUITE_data/primary_archive/primary_archive_dict-1.0/src/primary_archive_dict.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2009. All Rights Reserved. +%% Copyright Ericsson AB 2008-2016. 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/lib/kernel/test/erl_prim_loader_SUITE_data/primary_archive/primary_archive_dict-1.0/src/primary_archive_dict_app.erl b/lib/kernel/test/erl_prim_loader_SUITE_data/primary_archive/primary_archive_dict-1.0/src/primary_archive_dict_app.erl index 075632ab95..78e4f61579 100644 --- a/lib/kernel/test/erl_prim_loader_SUITE_data/primary_archive/primary_archive_dict-1.0/src/primary_archive_dict_app.erl +++ b/lib/kernel/test/erl_prim_loader_SUITE_data/primary_archive/primary_archive_dict-1.0/src/primary_archive_dict_app.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2009. All Rights Reserved. +%% Copyright Ericsson AB 2008-2016. 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/lib/kernel/test/erl_prim_loader_SUITE_data/primary_archive/primary_archive_dict-1.0/src/primary_archive_dict_sup.erl b/lib/kernel/test/erl_prim_loader_SUITE_data/primary_archive/primary_archive_dict-1.0/src/primary_archive_dict_sup.erl index 12fe90aaab..e63e958daf 100644 --- a/lib/kernel/test/erl_prim_loader_SUITE_data/primary_archive/primary_archive_dict-1.0/src/primary_archive_dict_sup.erl +++ b/lib/kernel/test/erl_prim_loader_SUITE_data/primary_archive/primary_archive_dict-1.0/src/primary_archive_dict_sup.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2009. All Rights Reserved. +%% Copyright Ericsson AB 2008-2016. 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/lib/kernel/test/erl_prim_loader_SUITE_data/primary_archive/primary_archive_dummy/src/primary_archive_dummy.erl b/lib/kernel/test/erl_prim_loader_SUITE_data/primary_archive/primary_archive_dummy/src/primary_archive_dummy.erl index 186e752c3d..4adbec3c81 100644 --- a/lib/kernel/test/erl_prim_loader_SUITE_data/primary_archive/primary_archive_dummy/src/primary_archive_dummy.erl +++ b/lib/kernel/test/erl_prim_loader_SUITE_data/primary_archive/primary_archive_dummy/src/primary_archive_dummy.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2009. All Rights Reserved. +%% Copyright Ericsson AB 2008-2016. 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/lib/kernel/test/erl_prim_loader_SUITE_data/primary_archive/primary_archive_dummy/src/primary_archive_dummy_app.erl b/lib/kernel/test/erl_prim_loader_SUITE_data/primary_archive/primary_archive_dummy/src/primary_archive_dummy_app.erl index 4a29c86a89..e90776ce73 100644 --- a/lib/kernel/test/erl_prim_loader_SUITE_data/primary_archive/primary_archive_dummy/src/primary_archive_dummy_app.erl +++ b/lib/kernel/test/erl_prim_loader_SUITE_data/primary_archive/primary_archive_dummy/src/primary_archive_dummy_app.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2009. All Rights Reserved. +%% Copyright Ericsson AB 2008-2016. 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/lib/kernel/test/erl_prim_loader_SUITE_data/primary_archive/primary_archive_dummy/src/primary_archive_dummy_sup.erl b/lib/kernel/test/erl_prim_loader_SUITE_data/primary_archive/primary_archive_dummy/src/primary_archive_dummy_sup.erl index c8cee46d08..145de8167f 100644 --- a/lib/kernel/test/erl_prim_loader_SUITE_data/primary_archive/primary_archive_dummy/src/primary_archive_dummy_sup.erl +++ b/lib/kernel/test/erl_prim_loader_SUITE_data/primary_archive/primary_archive_dummy/src/primary_archive_dummy_sup.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2009. All Rights Reserved. +%% Copyright Ericsson AB 2008-2016. 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/lib/kernel/test/error_handler_SUITE.erl b/lib/kernel/test/error_handler_SUITE.erl index 2a86d39b74..c6d457f314 100644 --- a/lib/kernel/test/error_handler_SUITE.erl +++ b/lib/kernel/test/error_handler_SUITE.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2013. All Rights Reserved. +%% Copyright Ericsson AB 2013-2016. 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/. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at %% -%% 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. +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% @@ -25,7 +26,9 @@ %% Callback from error_handler. -export(['$handle_undefined_function'/2]). -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap,{minutes,1}}]. all() -> [undefined_function_handler]. diff --git a/lib/kernel/test/error_logger_SUITE.erl b/lib/kernel/test/error_logger_SUITE.erl index 05bf5aae18..eab72e58a7 100644 --- a/lib/kernel/test/error_logger_SUITE.erl +++ b/lib/kernel/test/error_logger_SUITE.erl @@ -1,24 +1,25 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2011. All Rights Reserved. +%% Copyright Ericsson AB 1996-2018. 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/. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at %% -%% 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. +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% -module(error_logger_SUITE). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). %%----------------------------------------------------------------- %% We don't have to test the normal behaviour here, i.e. the tty @@ -29,29 +30,37 @@ -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, + off_heap/1, error_report/1, info_report/1, error/1, info/1, - emulator/1, tty/1, logfile/1, add/1, delete/1]). + emulator/1, via_logger_process/1, other_node/1, + tty/1, logfile/1, add/1, delete/1, format_depth/1]). --export([generate_error/0]). +-export([generate_error/2]). -export([init/1, handle_event/2, handle_call/2, handle_info/2, terminate/2]). -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap,{minutes,1}}]. all() -> - [error_report, info_report, error, info, emulator, tty, - logfile, add, delete]. + [off_heap, error_report, info_report, error, info, emulator, + via_logger_process, other_node, tty, logfile, add, delete, + format_depth]. groups() -> []. init_per_suite(Config) -> + logger:add_handler(error_logger,error_logger, + #{level=>info,filter_default=>log}), Config. end_per_suite(_Config) -> + logger:remove_handler(error_logger), ok. init_per_group(_GroupName, Config) -> @@ -63,212 +72,256 @@ end_per_group(_GroupName, Config) -> %%----------------------------------------------------------------- -error_report(suite) -> []; -error_report(doc) -> []; +off_heap(_Config) -> + %% The error_logger process may receive a huge amount of + %% messages. Make sure that they are stored off heap to + %% avoid exessive GCs. + MQD = message_queue_data, + {MQD,off_heap} = process_info(whereis(error_logger), MQD), + ok. + +%%----------------------------------------------------------------- + error_report(Config) when is_list(Config) -> - ?line error_logger:add_report_handler(?MODULE, self()), + error_logger:add_report_handler(?MODULE, self()), Rep1 = [{tag1,"data1"},{tag2,data2},{tag3,3}], Rep2 = [testing,"testing",{tag1,"tag1"}], Rep3 = "This is a string !", Rep4 = {this,is,a,tuple}, - ?line ok = error_logger:error_report(Rep1), + ok = error_logger:error_report(Rep1), reported(error_report, std_error, Rep1), - ?line ok = error_logger:error_report(Rep2), + ok = error_logger:error_report(Rep2), reported(error_report, std_error, Rep2), - ?line ok = error_logger:error_report(Rep3), + ok = error_logger:error_report(Rep3), reported(error_report, std_error, Rep3), - ?line ok = error_logger:error_report(Rep4), + ok = error_logger:error_report(Rep4), reported(error_report, std_error, Rep4), - ?line ok = error_logger:error_report(test_type, Rep1), + ok = error_logger:error_report(test_type, Rep1), reported(error_report, test_type, Rep1), - ?line ok = error_logger:error_report(test_type, Rep2), + ok = error_logger:error_report(test_type, Rep2), reported(error_report, test_type, Rep2), - ?line ok = error_logger:error_report(test_type, Rep3), + ok = error_logger:error_report(test_type, Rep3), reported(error_report, test_type, Rep3), - ?line ok = error_logger:error_report(test_type, Rep4), + ok = error_logger:error_report(test_type, Rep4), reported(error_report, test_type, Rep4), - ?line ok = error_logger:error_report("test_type", Rep1), + ok = error_logger:error_report("test_type", Rep1), reported(error_report, "test_type", Rep1), - ?line ok = error_logger:error_report({test,type}, Rep2), + ok = error_logger:error_report({test,type}, Rep2), reported(error_report, {test,type}, Rep2), - ?line ok = error_logger:error_report([test,type], Rep3), + ok = error_logger:error_report([test,type], Rep3), reported(error_report, [test,type], Rep3), - ?line ok = error_logger:error_report(1, Rep4), + ok = error_logger:error_report(1, Rep4), reported(error_report, 1, Rep4), - ?line my_yes = error_logger:delete_report_handler(?MODULE), + my_yes = error_logger:delete_report_handler(?MODULE), ok. %%----------------------------------------------------------------- -info_report(suite) -> []; -info_report(doc) -> []; info_report(Config) when is_list(Config) -> - ?line error_logger:add_report_handler(?MODULE, self()), + error_logger:add_report_handler(?MODULE, self()), Rep1 = [{tag1,"data1"},{tag2,data2},{tag3,3}], Rep2 = [testing,"testing",{tag1,"tag1"}], Rep3 = "This is a string !", Rep4 = {this,is,a,tuple}, - ?line ok = error_logger:info_report(Rep1), + ok = error_logger:info_report(Rep1), reported(info_report, std_info, Rep1), - ?line ok = error_logger:info_report(Rep2), + ok = error_logger:info_report(Rep2), reported(info_report, std_info, Rep2), - ?line ok = error_logger:info_report(Rep3), + ok = error_logger:info_report(Rep3), reported(info_report, std_info, Rep3), - ?line ok = error_logger:info_report(Rep4), + ok = error_logger:info_report(Rep4), reported(info_report, std_info, Rep4), - ?line ok = error_logger:info_report(test_type, Rep1), + ok = error_logger:info_report(test_type, Rep1), reported(info_report, test_type, Rep1), - ?line ok = error_logger:info_report(test_type, Rep2), + ok = error_logger:info_report(test_type, Rep2), reported(info_report, test_type, Rep2), - ?line ok = error_logger:info_report(test_type, Rep3), + ok = error_logger:info_report(test_type, Rep3), reported(info_report, test_type, Rep3), - ?line ok = error_logger:info_report(test_type, Rep4), + ok = error_logger:info_report(test_type, Rep4), reported(info_report, test_type, Rep4), - ?line ok = error_logger:info_report("test_type", Rep1), + ok = error_logger:info_report("test_type", Rep1), reported(info_report, "test_type", Rep1), - ?line ok = error_logger:info_report({test,type}, Rep2), + ok = error_logger:info_report({test,type}, Rep2), reported(info_report, {test,type}, Rep2), - ?line ok = error_logger:info_report([test,type], Rep3), + ok = error_logger:info_report([test,type], Rep3), reported(info_report, [test,type], Rep3), - ?line ok = error_logger:info_report(1, Rep4), + ok = error_logger:info_report(1, Rep4), reported(info_report, 1, Rep4), - ?line my_yes = error_logger:delete_report_handler(?MODULE), + my_yes = error_logger:delete_report_handler(?MODULE), ok. %%----------------------------------------------------------------- -error(suite) -> []; -error(doc) -> []; error(Config) when is_list(Config) -> - ?line error_logger:add_report_handler(?MODULE, self()), + error_logger:add_report_handler(?MODULE, self()), Msg1 = "This is a plain text string~n", Msg2 = "This is a text with arguments ~p~n", Arg2 = "This is the argument", Msg3 = {erroneous,msg}, - ?line ok = error_logger:error_msg(Msg1), + ok = error_logger:error_msg(Msg1), reported(error, Msg1, []), - ?line ok = error_logger:error_msg(Msg2, Arg2), + ok = error_logger:error_msg(Msg2, Arg2), reported(error, Msg2, Arg2), - ?line ok = error_logger:error_msg(Msg3), + ok = error_logger:error_msg(Msg3), reported(error, Msg3, []), - ?line ok = error_logger:error_msg(Msg1, []), + ok = error_logger:error_msg(Msg1, []), reported(error, Msg1, []), - ?line ok = error_logger:error_msg(Msg2, Arg2), + ok = error_logger:error_msg(Msg2, Arg2), reported(error, Msg2, Arg2), - ?line ok = error_logger:error_msg(Msg3, []), + ok = error_logger:error_msg(Msg3, []), reported(error, Msg3, []), - ?line ok = error_logger:format(Msg1, []), + ok = error_logger:format(Msg1, []), reported(error, Msg1, []), - ?line ok = error_logger:format(Msg2, Arg2), + ok = error_logger:format(Msg2, Arg2), reported(error, Msg2, Arg2), - ?line ok = error_logger:format(Msg3, []), + ok = error_logger:format(Msg3, []), reported(error, Msg3, []), - ?line my_yes = error_logger:delete_report_handler(?MODULE), + my_yes = error_logger:delete_report_handler(?MODULE), ok. %%----------------------------------------------------------------- -info(suite) -> []; -info(doc) -> []; info(Config) when is_list(Config) -> - ?line error_logger:add_report_handler(?MODULE, self()), + error_logger:add_report_handler(?MODULE, self()), Msg1 = "This is a plain text string~n", Msg2 = "This is a text with arguments ~p~n", Arg2 = "This is the argument", Msg3 = {erroneous,msg}, - ?line ok = error_logger:info_msg(Msg1), + ok = error_logger:info_msg(Msg1), reported(info_msg, Msg1, []), - ?line ok = error_logger:info_msg(Msg2, Arg2), + ok = error_logger:info_msg(Msg2, Arg2), reported(info_msg, Msg2, Arg2), - ?line ok = error_logger:info_msg(Msg3), + ok = error_logger:info_msg(Msg3), reported(info_msg, Msg3, []), - ?line ok = error_logger:info_msg(Msg1, []), + ok = error_logger:info_msg(Msg1, []), reported(info_msg, Msg1, []), - ?line ok = error_logger:info_msg(Msg2, Arg2), + ok = error_logger:info_msg(Msg2, Arg2), reported(info_msg, Msg2, Arg2), - ?line ok = error_logger:info_msg(Msg3, []), + ok = error_logger:info_msg(Msg3, []), reported(info_msg, Msg3, []), - ?line my_yes = error_logger:delete_report_handler(?MODULE), + my_yes = error_logger:delete_report_handler(?MODULE), ok. %%----------------------------------------------------------------- -emulator(suite) -> []; -emulator(doc) -> []; emulator(Config) when is_list(Config) -> - ?line error_logger:add_report_handler(?MODULE, self()), - spawn(?MODULE, generate_error, []), - reported(emulator), - ?line my_yes = error_logger:delete_report_handler(?MODULE), + error_logger:add_report_handler(?MODULE, self()), + Msg = "Error in process ~p on node ~p with exit value:~n~p~n", + Error = {badmatch,4}, + Stack = [{module, function, 2, []}], + Pid = spawn(?MODULE, generate_error, [Error, Stack]), + reported(error, Msg, [Pid, node(), {Error, Stack}]), + my_yes = error_logger:delete_report_handler(?MODULE), + ok. + +generate_error(Error, Stack) -> + erlang:raise(error, Error, Stack). + +%%----------------------------------------------------------------- + +via_logger_process(Config) -> + case os:type() of + {win32,_} -> + {skip,"Skip on windows - cant change file mode"}; + _ -> + error_logger:add_report_handler(?MODULE, self()), + Dir = filename:join(?config(priv_dir,Config),"dummydir"), + Msg = "File operation error: eacces. Target: " ++ + Dir ++ ". Function: list_dir. ", + ok = file:make_dir(Dir), + ok = file:change_mode(Dir,8#0222), + error = erl_prim_loader:list_dir(Dir), + ok = file:change_mode(Dir,8#0664), + _ = file:del_dir(Dir), + reported(error_report, std_error, Msg), + my_yes = error_logger:delete_report_handler(?MODULE), + ok + end. + +%%----------------------------------------------------------------- + +other_node(_Config) -> + error_logger:add_report_handler(?MODULE, self()), + {ok,Node} = test_server:start_node(?FUNCTION_NAME,slave,[]), + ok = rpc:call(Node,logger,add_handler,[error_logger,error_logger, + #{level=>info,filter_default=>log}]), + rpc:call(Node,error_logger,error_report,[hi_from_remote]), + reported(error_report,std_error,hi_from_remote), + test_server:stop_node(Node), ok. -generate_error() -> - erlang:error({badmatch,4}). %%----------------------------------------------------------------- %% We don't enables or disables tty error logging here. We do not %% want to interact with the test run. %%----------------------------------------------------------------- -tty(suite) -> []; -tty(doc) -> []; tty(Config) when is_list(Config) -> - ?line {'EXIT', _Reason} = (catch error_logger:tty(dummy)), + {'EXIT', _Reason} = (catch error_logger:tty(dummy)), ok. %%----------------------------------------------------------------- %% If where already exists a logfile we skip this test case !! %%----------------------------------------------------------------- -logfile(suite) -> []; -logfile(doc) -> []; logfile(Config) when is_list(Config) -> - ?line case error_logger:logfile(filename) of - {error, no_log_file} -> % Ok, we continues. - do_logfile(); - _ -> - ok - end. + case error_logger:logfile(filename) of + {error, no_log_file} -> % Ok, we continues. + do_logfile(); + _ -> + ok + end. do_logfile() -> - ?line {error, _} = error_logger:logfile(close), - ?line {error, _} = error_logger:logfile({open,{error}}), - ?line ok = error_logger:logfile({open, "dummy_logfile.log"}), - ?line "dummy_logfile.log" = error_logger:logfile(filename), - ?line ok = error_logger:logfile(close), - ?line {'EXIT',_} = (catch error_logger:logfile(dummy)), + {error, _} = error_logger:logfile(close), + {error, _} = error_logger:logfile({open,{error}}), + ok = error_logger:logfile({open, "dummy_logfile.log"}), + "dummy_logfile.log" = error_logger:logfile(filename), + ok = error_logger:logfile(close), + {'EXIT',_} = (catch error_logger:logfile(dummy)), ok. %%----------------------------------------------------------------- -add(suite) -> []; -add(doc) -> []; add(Config) when is_list(Config) -> - ?line {'EXIT',_} = (catch error_logger:add_report_handler("dummy")), - ?line {'EXIT',_} = error_logger:add_report_handler(non_existing), - ?line my_error = error_logger:add_report_handler(?MODULE, [error]), + {'EXIT',_} = (catch error_logger:add_report_handler("dummy")), + {'EXIT',_} = error_logger:add_report_handler(non_existing), + my_error = error_logger:add_report_handler(?MODULE, [error]), ok. %%----------------------------------------------------------------- -delete(suite) -> []; -delete(doc) -> []; delete(Config) when is_list(Config) -> - ?line {'EXIT',_} = (catch error_logger:delete_report_handler("dummy")), - ?line {error,_} = error_logger:delete_report_handler(non_existing), + {'EXIT',_} = (catch error_logger:delete_report_handler("dummy")), + {error,_} = error_logger:delete_report_handler(non_existing), + ok. + +%%----------------------------------------------------------------- + +format_depth(_Config) -> + ok = application:set_env(kernel,error_logger_format_depth,30), + 30 = error_logger:get_format_depth(), + ok = application:set_env(kernel,error_logger_format_depth,3), + 10 = error_logger:get_format_depth(), + ok = application:set_env(kernel,error_logger_format_depth,11), + 11 = error_logger:get_format_depth(), + ok = application:set_env(kernel,error_logger_format_depth,unlimited), + unlimited = error_logger:get_format_depth(), + ok = application:unset_env(kernel,error_logger_format_depth), + unlimited = error_logger:get_format_depth(), ok. %%----------------------------------------------------------------- @@ -280,16 +333,7 @@ reported(Tag, Type, Report) -> test_server:messages_get(), ok after 1000 -> - test_server:fail(no_report_received) - end. - -reported(emulator) -> - receive - {error, "~s~n", String} when is_list(String) -> - test_server:messages_get(), - ok - after 1000 -> - test_server:fail(no_report_received) + ct:fail({no_report_received,test_server:messages_get()}) end. %%----------------------------------------------------------------- diff --git a/lib/kernel/test/error_logger_warn_SUITE.erl b/lib/kernel/test/error_logger_warn_SUITE.erl index 2bf467610e..8f1eb2ba0a 100644 --- a/lib/kernel/test/error_logger_warn_SUITE.erl +++ b/lib/kernel/test/error_logger_warn_SUITE.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2003-2012. All Rights Reserved. +%% Copyright Ericsson AB 2003-2018. 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% @@ -21,14 +22,14 @@ -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, init_per_testcase/2,end_per_testcase/2, - basic/1,warnings_info/1,warnings_warnings/1, - rb_basic/1,rb_warnings_info/1,rb_warnings_warnings/1, + basic/1,warnings_info/1,warnings_errors/1, + rb_basic/1,rb_warnings_info/1,rb_warnings_errors/1, rb_trunc/1,rb_utc/1,file_utc/1]). %% Internal exports. -export([init/1,handle_event/2,handle_info/2,handle_call/2]). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -define(EXPECT(Pattern), (fun() -> @@ -41,15 +42,14 @@ end end)()). -% Default timetrap timeout (set in init_per_testcase). --define(default_timeout, ?t:minutes(1)). - -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap,{minutes,1}}]. all() -> - [basic, warnings_info, warnings_warnings, rb_basic, - rb_warnings_info, rb_warnings_warnings, rb_trunc, + [basic, warnings_info, warnings_errors, rb_basic, + rb_warnings_info, rb_warnings_errors, rb_trunc, rb_utc, file_utc]. groups() -> @@ -69,69 +69,58 @@ end_per_group(_GroupName, Config) -> init_per_testcase(_Case, Config) -> - ?line Dog = ?t:timetrap(?default_timeout), - [{watchdog, Dog} | Config]. -end_per_testcase(_Case, Config) -> - Dog = ?config(watchdog, Config), - test_server:timetrap_cancel(Dog), + Config. + +end_per_testcase(_Case, _Config) -> ok. -basic(doc) -> - ["Tests basic error logger functionality"]; +%% Tests basic error logger functionality. basic(Config) when is_list(Config) -> put(elw_config,Config), basic(). -warnings_info(doc) -> - ["Tests mapping warnings to info functionality"]; +%% Tests mapping warnings to info functionality. warnings_info(Config) when is_list(Config) -> put(elw_config,Config), warnings_info(). -warnings_warnings(doc) -> - ["Tests mapping warnings to warnings functionality"]; -warnings_warnings(Config) when is_list(Config) -> +%% Tests mapping warnings to errors functionality. +warnings_errors(Config) when is_list(Config) -> put(elw_config,Config), - warnings_warnings(). + warnings_errors(). -rb_basic(doc) -> - ["Tests basic rb functionality"]; +%% Tests basic rb functionality. rb_basic(Config) when is_list(Config) -> put(elw_config,Config), rb_basic(). -rb_warnings_info(doc) -> - ["Tests warnings as info rb functionality"]; +%% Tests warnings as info rb functionality. rb_warnings_info(Config) when is_list(Config) -> put(elw_config,Config), rb_warnings_info(). -rb_warnings_warnings(doc) -> - ["Tests warnings as warnings rb functionality"]; -rb_warnings_warnings(Config) when is_list(Config) -> +%% Tests warnings as errors rb functionality. +rb_warnings_errors(Config) when is_list(Config) -> put(elw_config,Config), - rb_warnings_warnings(). + rb_warnings_errors(). -rb_trunc(doc) -> - ["Tests rb functionality on truncated data"]; +%% Tests rb functionality on truncated data. rb_trunc(Config) when is_list(Config) -> put(elw_config,Config), rb_trunc(). -rb_utc(doc) -> - ["Tests UTC mapping in rb (-sasl utc_log true)"]; +%% Tests UTC mapping in rb (-sasl utc_log true). rb_utc(Config) when is_list(Config) -> put(elw_config,Config), rb_utc(). -file_utc(doc) -> - ["Tests UTC mapping in file logger (-stdlib utc_log true)"]; +%% Tests UTC mapping in file logger (-stdlib utc_log true). file_utc(Config) when is_list(Config) -> put(elw_config,Config), file_utc(). -% a small gen_event +%% a small gen_event init([Pid]) -> {ok, Pid}. @@ -159,6 +148,9 @@ install_relay(Node) -> rpc:call(Node,error_logger,add_report_handler,[?MODULE,[self()]]). +warning_map(Node) -> + rpc:call(Node,error_logger,warning_map,[]). + format(Node,A,B) -> rpc:call(Node,error_logger,format,[A,B]). error_msg(Node,A,B) -> @@ -181,80 +173,81 @@ nn() -> basic() -> - ?line Node = start_node(nn(),[]), - ?line ok = install_relay(Node), - ?line Self = self(), - ?line GL = group_leader(), - ?line format(Node,"~p~n",[Self]), - ?line ?EXPECT({handle_event,{error,GL,{_,"~p~n",[Self]}}}), - ?line error_msg(Node,"~p~n",[Self]), - ?line ?EXPECT({handle_event,{error,GL,{_,"~p~n",[Self]}}}), - ?line warning_msg(Node,"~p~n",[Self]), - ?line ?EXPECT({handle_event,{error,GL,{_,"~p~n",[Self]}}}), - ?line info_msg(Node,"~p~n",[Self]), - ?line ?EXPECT({handle_event,{info_msg,GL,{_,"~p~n",[Self]}}}), - ?line Report = [{self,Self},{gl,GL},make_ref()], - ?line error_report(Node,Report), - ?line ?EXPECT({handle_event,{error_report,GL,{_,std_error,Report}}}), - ?line warning_report(Node,Report), - ?line ?EXPECT({handle_event,{error_report,GL,{_,std_error,Report}}}), - ?line info_report(Node,Report), - ?line ?EXPECT({handle_event,{info_report,GL,{_,std_info,Report}}}), - - ?line stop_node(Node), + Node = start_node(nn(),[]), + ok = install_relay(Node), + Self = self(), + GL = group_leader(), + warning = warning_map(Node), + format(Node,"~p~n",[Self]), + ?EXPECT({handle_event,{error,GL,{_,"~p~n",[Self]}}}), + error_msg(Node,"~p~n",[Self]), + ?EXPECT({handle_event,{error,GL,{_,"~p~n",[Self]}}}), + warning_msg(Node,"~p~n",[Self]), + ?EXPECT({handle_event,{warning_msg,GL,{_,"~p~n",[Self]}}}), + info_msg(Node,"~p~n",[Self]), + ?EXPECT({handle_event,{info_msg,GL,{_,"~p~n",[Self]}}}), + Report = [{self,Self},{gl,GL},make_ref()], + error_report(Node,Report), + ?EXPECT({handle_event,{error_report,GL,{_,std_error,Report}}}), + warning_report(Node,Report), + ?EXPECT({handle_event,{warning_report,GL,{_,std_warning,Report}}}), + info_report(Node,Report), + ?EXPECT({handle_event,{info_report,GL,{_,std_info,Report}}}), + stop_node(Node), ok. warnings_info() -> - ?line Node = start_node(nn(),"+Wi"), - ?line ok = install_relay(Node), - ?line Self = self(), - ?line GL = group_leader(), - ?line Report = [{self,Self},{gl,GL},make_ref()], - ?line warning_msg(Node,"~p~n",[Self]), - ?line ?EXPECT({handle_event,{info_msg,GL,{_,"~p~n",[Self]}}}), - ?line warning_report(Node,Report), - ?line ?EXPECT({handle_event,{info_report,GL,{_,std_info,Report}}}), - ?line stop_node(Node), + Node = start_node(nn(),"+Wi"), + ok = install_relay(Node), + Self = self(), + GL = group_leader(), + info = warning_map(Node), + Report = [{self,Self},{gl,GL},make_ref()], + warning_msg(Node,"~p~n",[Self]), + ?EXPECT({handle_event,{info_msg,GL,{_,"~p~n",[Self]}}}), + warning_report(Node,Report), + ?EXPECT({handle_event,{info_report,GL,{_,std_info,Report}}}), + stop_node(Node), ok. -warnings_warnings() -> - ?line Node = start_node(nn(),"+Ww"), - ?line ok = install_relay(Node), - ?line Self = self(), - ?line GL = group_leader(), - ?line Report = [{self,Self},{gl,GL},make_ref()], - ?line warning_msg(Node,"~p~n",[Self]), - ?line ?EXPECT({handle_event,{warning_msg,GL,{_,"~p~n",[Self]}}}), - ?line warning_report(Node,Report), - ?line ?EXPECT({handle_event,{warning_report,GL,{_,std_warning,Report}}}), - ?line stop_node(Node), +warnings_errors() -> + Node = start_node(nn(),"+We"), + ok = install_relay(Node), + Self = self(), + GL = group_leader(), + error = warning_map(Node), + Report = [{self,Self},{gl,GL},make_ref()], + warning_msg(Node,"~p~n",[Self]), + ?EXPECT({handle_event,{error,GL,{_,"~p~n",[Self]}}}), + warning_report(Node,Report), + ?EXPECT({handle_event,{error_report,GL,{_,std_error,Report}}}), + stop_node(Node), ok. - - -% RB... + +%% RB... quote(String) -> case os:type() of - {win32,_} -> - "\\\""++String++"\\\""; - _ -> - "'\""++String++"\"'" + {win32,_} -> + "\\\""++String++"\\\""; + _ -> + "'\""++String++"\"'" end. iquote(String) -> case os:type() of - {win32,_} -> - "\\\""++String++"\\\""; - _ -> - "\""++String++"\"" + {win32,_} -> + "\\\""++String++"\\\""; + _ -> + "\""++String++"\"" end. oquote(String) -> case os:type() of - {win32,_} -> - "\""++String++"\""; - _ -> - "'"++String++"'" + {win32,_} -> + "\""++String++"\""; + _ -> + "'"++String++"'" end. @@ -264,21 +257,21 @@ findstr(String,FileName) -> findstrc(String,File) -> case string:str(File,String) of - N when is_integer(N), - N > 0 -> - S2 = lists:sublist(File,N,length(File)), - case string:str(S2,"\n") of - 0 -> - 1; - M -> - S3 = lists:sublist(S2,M,length(S2)), - 1 + findstrc(String,S3) - end; - _ -> - 0 + N when is_integer(N), + N > 0 -> + S2 = lists:sublist(File,N,length(File)), + case string:str(S2,"\n") of + 0 -> + 1; + M -> + S3 = lists:sublist(S2,M,length(S2)), + 1 + findstrc(String,S3) + end; + _ -> + 0 end. -% Doesn't count empty lines +%% Doesn't count empty lines lines(File) -> length( string:tokens( @@ -286,17 +279,17 @@ lines(File) -> element(2,file:read_file(File))), "\n")). -%directories anf filenames +%% Directories and filenames ld() -> Config = get(elw_config), - PrivDir = ?config(priv_dir, Config), + PrivDir = proplists:get_value(priv_dir, Config), filename:absname(PrivDir). lf() -> filename:join([ld(),"logfile.txt"]). rd() -> Config = get(elw_config), - PrivDir = ?config(priv_dir, Config), + PrivDir = proplists:get_value(priv_dir, Config), LogDir = filename:join(PrivDir,"log"), file:make_dir(LogDir), filename:absname(LogDir). @@ -310,7 +303,7 @@ nice_stop_node(Name) -> {nodedown,Name} -> ok end. -%clean out rd() before each report test in order to get only one file... +%% Clean out rd() before each report test in order to get only one file... clean_rd() -> {ok,L} = file:list_dir(rd()), lists:foreach(fun(F) -> @@ -347,177 +340,190 @@ one_rb_findstr(Param,String) -> rb:stop_log(), findstr(String,lf()). -% Tests +%% Tests rb_basic() -> - ?line clean_rd(), - % Behold, the magic parameters to activate rb logging... - ?line Node = start_node(nn(),"-boot start_sasl -sasl error_logger_mf_dir "++ - quote(rd())++" error_logger_mf_maxbytes 5000 " - "error_logger_mf_maxfiles 5"), - ?line Self = self(), - ?line GL = group_leader(), - ?line Report = [{self,Self},{gl,GL},make_ref()], - ?line fake_gl(Node,warning_msg,"~p~n",[Self]), - ?line fake_gl(Node,warning_report,Report), - ?line nice_stop_node(Node), - ?line application:start(sasl), - ?line rb:start([{report_dir, rd()}]), - ?line rb:list(), - ?line true = (one_rb_lines([error]) > 1), - ?line true = (one_rb_lines([error_report]) > 1), - ?line 1 = one_rb_findstr([error],pid_to_list(Self)), - ?line 1 = one_rb_findstr([error_report],pid_to_list(Self)), - ?line 2 = one_rb_findstr([],pid_to_list(Self)), - ?line true = (one_rb_findstr([progress],"===") > 4), - ?line rb:stop(), - ?line application:stop(sasl), - ?line stop_node(Node), + clean_rd(), + %% Behold, the magic parameters to activate rb logging... + Node = start_node(nn(),"-boot start_sasl -sasl error_logger_mf_dir "++ + quote(rd())++" error_logger_mf_maxbytes 5000 " + "error_logger_mf_maxfiles 5"), + Self = self(), + GL = group_leader(), + warning = warning_map(Node), + Report = [{self,Self},{gl,GL},make_ref()], + fake_gl(Node,warning_msg,"~p~n",[Self]), + fake_gl(Node,warning_report,Report), + nice_stop_node(Node), + application:start(sasl), + rb:start([{report_dir, rd()}]), + rb:list(), + true = (one_rb_lines([error]) =:= 0), + true = (one_rb_lines([error_report]) =:= 0), + 0 = one_rb_findstr([error],pid_to_list(Self)), + 0 = one_rb_findstr([error_report],pid_to_list(Self)), + 1 = one_rb_findstr([warning_msg],pid_to_list(Self)), + 1 = one_rb_findstr([warning_report],pid_to_list(Self)), + 0 = one_rb_findstr([info_msg],pid_to_list(Self)), + 0 = one_rb_findstr([info_report],pid_to_list(Self)), + 2 = one_rb_findstr([],pid_to_list(Self)), + true = (one_rb_findstr([progress],"===") > 3), + rb:stop(), + application:stop(sasl), + stop_node(Node), ok. rb_warnings_info() -> - ?line clean_rd(), - ?line Node = start_node(nn(),"+W i -boot start_sasl -sasl error_logger_mf_dir "++ - quote(rd())++" error_logger_mf_maxbytes 5000 " - "error_logger_mf_maxfiles 5"), - ?line Self = self(), - ?line GL = group_leader(), - ?line Report = [{self,Self},{gl,GL},make_ref()], - ?line fake_gl(Node,warning_msg,"~p~n",[Self]), - ?line fake_gl(Node,warning_report,Report), - ?line nice_stop_node(Node), - ?line application:start(sasl), - ?line rb:start([{report_dir, rd()}]), - ?line rb:list(), - ?line true = (one_rb_lines([error]) =:= 0), - ?line true = (one_rb_lines([error_report]) =:= 0), - ?line 0 = one_rb_findstr([error],pid_to_list(Self)), - ?line 0 = one_rb_findstr([error_report],pid_to_list(Self)), - ?line 0 = one_rb_findstr([warning_msg],pid_to_list(Self)), - ?line 0 = one_rb_findstr([warning_report],pid_to_list(Self)), - ?line 1 = one_rb_findstr([info_msg],pid_to_list(Self)), - ?line 1 = one_rb_findstr([info_report],pid_to_list(Self)), - ?line 2 = one_rb_findstr([],pid_to_list(Self)), - ?line true = (one_rb_findstr([progress],"===") > 4), - ?line rb:stop(), - ?line application:stop(sasl), - ?line stop_node(Node), + clean_rd(), + Node = start_node(nn(),"+W i -boot start_sasl -sasl error_logger_mf_dir "++ + quote(rd())++" error_logger_mf_maxbytes 5000 " + "error_logger_mf_maxfiles 5"), + Self = self(), + GL = group_leader(), + info = warning_map(Node), + Report = [{self,Self},{gl,GL},make_ref()], + fake_gl(Node,warning_msg,"~p~n",[Self]), + fake_gl(Node,warning_report,Report), + nice_stop_node(Node), + application:start(sasl), + rb:start([{report_dir, rd()}]), + rb:list(), + true = (one_rb_lines([error]) =:= 0), + true = (one_rb_lines([error_report]) =:= 0), + 0 = one_rb_findstr([error],pid_to_list(Self)), + 0 = one_rb_findstr([error_report],pid_to_list(Self)), + 0 = one_rb_findstr([warning_msg],pid_to_list(Self)), + 0 = one_rb_findstr([warning_report],pid_to_list(Self)), + 1 = one_rb_findstr([info_msg],pid_to_list(Self)), + 1 = one_rb_findstr([info_report],pid_to_list(Self)), + 2 = one_rb_findstr([],pid_to_list(Self)), + true = (one_rb_findstr([progress],"===") > 3), + rb:stop(), + application:stop(sasl), + stop_node(Node), ok. -rb_warnings_warnings() -> - ?line clean_rd(), - ?line Node = start_node(nn(),"+W w -boot start_sasl -sasl error_logger_mf_dir "++ - quote(rd())++" error_logger_mf_maxbytes 5000 " - "error_logger_mf_maxfiles 5"), - ?line Self = self(), - ?line GL = group_leader(), - ?line Report = [{self,Self},{gl,GL},make_ref()], - ?line fake_gl(Node,warning_msg,"~p~n",[Self]), - ?line fake_gl(Node,warning_report,Report), - ?line nice_stop_node(Node), - ?line application:start(sasl), - ?line rb:start([{report_dir, rd()}]), - ?line rb:list(), - ?line true = (one_rb_lines([error]) =:= 0), - ?line true = (one_rb_lines([error_report]) =:= 0), - ?line 0 = one_rb_findstr([error],pid_to_list(Self)), - ?line 0 = one_rb_findstr([error_report],pid_to_list(Self)), - ?line 1 = one_rb_findstr([warning_msg],pid_to_list(Self)), - ?line 1 = one_rb_findstr([warning_report],pid_to_list(Self)), - ?line 0 = one_rb_findstr([info_msg],pid_to_list(Self)), - ?line 0 = one_rb_findstr([info_report],pid_to_list(Self)), - ?line 2 = one_rb_findstr([],pid_to_list(Self)), - ?line true = (one_rb_findstr([progress],"===") > 4), - ?line rb:stop(), - ?line application:stop(sasl), - ?line stop_node(Node), +rb_warnings_errors() -> + clean_rd(), + Node = start_node(nn(),"+W e -boot start_sasl -sasl error_logger_mf_dir "++ + quote(rd())++" error_logger_mf_maxbytes 5000 " + "error_logger_mf_maxfiles 5"), + Self = self(), + GL = group_leader(), + error = warning_map(Node), + Report = [{self,Self},{gl,GL},make_ref()], + fake_gl(Node,warning_msg,"~p~n",[Self]), + fake_gl(Node,warning_report,Report), + nice_stop_node(Node), + application:start(sasl), + rb:start([{report_dir, rd()}]), + rb:list(), + true = (one_rb_lines([error]) > 1), + true = (one_rb_lines([error_report]) > 1), + 1 = one_rb_findstr([error],pid_to_list(Self)), + 1 = one_rb_findstr([error_report],pid_to_list(Self)), + 0 = one_rb_findstr([warning_msg],pid_to_list(Self)), + 0 = one_rb_findstr([warning_report],pid_to_list(Self)), + 0 = one_rb_findstr([info_msg],pid_to_list(Self)), + 0 = one_rb_findstr([info_report],pid_to_list(Self)), + 2 = one_rb_findstr([],pid_to_list(Self)), + true = (one_rb_findstr([progress],"===") > 3), + rb:stop(), + application:stop(sasl), + stop_node(Node), ok. rb_trunc() -> - ?line clean_rd(), - ?line Node = start_node(nn(),"+W w -boot start_sasl -sasl error_logger_mf_dir "++ - quote(rd())++" error_logger_mf_maxbytes 5000 " - "error_logger_mf_maxfiles 5"), - ?line Self = self(), - ?line GL = group_leader(), - ?line Report = [{self,Self},{gl,GL},make_ref()], - ?line fake_gl(Node,warning_msg,"~p~n",[Self]), - ?line fake_gl(Node,warning_report,Report), - ?line nice_stop_node(Node), - ?line application:start(sasl), - ?line {ok,File} = file:read_file(rf()), - ?line S=byte_size(File)-2, - ?line <<TFile:S/binary,_/binary>>=File, - ?line file:write_file(rf(),TFile), - ?line rb:start([{report_dir, rd()}]), - ?line rb:list(), - ?line true = (one_rb_lines([error]) =:= 0), - ?line true = (one_rb_lines([error_report]) =:= 0), - ?line 0 = one_rb_findstr([error],pid_to_list(Self)), - ?line 0 = one_rb_findstr([error_report],pid_to_list(Self)), - ?line 1 = one_rb_findstr([warning_msg],pid_to_list(Self)), - ?line 0 = one_rb_findstr([warning_report],pid_to_list(Self)), - ?line 0 = one_rb_findstr([info_msg],pid_to_list(Self)), - ?line 0 = one_rb_findstr([info_report],pid_to_list(Self)), - ?line 1 = one_rb_findstr([],pid_to_list(Self)), - ?line true = (one_rb_findstr([progress],"===") > 4), - ?line rb:stop(), - ?line application:stop(sasl), - ?line stop_node(Node), + clean_rd(), + Node = start_node(nn(),"-boot start_sasl -sasl error_logger_mf_dir "++ + quote(rd())++" error_logger_mf_maxbytes 5000 " + "error_logger_mf_maxfiles 5"), + Self = self(), + GL = group_leader(), + Report = [{self,Self},{gl,GL},make_ref()], + fake_gl(Node,warning_msg,"~p~n",[Self]), + fake_gl(Node,warning_report,Report), + nice_stop_node(Node), + application:start(sasl), + {ok,File} = file:read_file(rf()), + S=byte_size(File)-2, + <<TFile:S/binary,_/binary>>=File, + file:write_file(rf(),TFile), + rb:start([{report_dir, rd()}]), + rb:list(), + true = (one_rb_lines([error]) =:= 0), + true = (one_rb_lines([error_report]) =:= 0), + 0 = one_rb_findstr([error],pid_to_list(Self)), + 0 = one_rb_findstr([error_report],pid_to_list(Self)), + 1 = one_rb_findstr([warning_msg],pid_to_list(Self)), + 0 = one_rb_findstr([warning_report],pid_to_list(Self)), + 0 = one_rb_findstr([info_msg],pid_to_list(Self)), + 0 = one_rb_findstr([info_report],pid_to_list(Self)), + 1 = one_rb_findstr([],pid_to_list(Self)), + true = (one_rb_findstr([progress],"===") > 3), + rb:stop(), + application:stop(sasl), + stop_node(Node), ok. rb_utc() -> - ?line clean_rd(), - ?line Node = start_node(nn(),"+W w -boot start_sasl -sasl error_logger_mf_dir "++ - quote(rd())++" error_logger_mf_maxbytes 5000 " - "error_logger_mf_maxfiles 5 -sasl utc_log true"), - ?line Self = self(), - ?line GL = group_leader(), - ?line Report = [{self,Self},{gl,GL},make_ref()], - ?line fake_gl(Node,warning_msg,"~p~n",[Self]), - ?line fake_gl(Node,warning_report,Report), - ?line nice_stop_node(Node), - ?line application:stop(sasl), - ?line UtcLog=case application:get_env(sasl,utc_log) of - {ok,true} -> - true; - _AllOthers -> - application:set_env(sasl,utc_log,true), - false - end, - ?line application:start(sasl), - ?line rb:start([{report_dir, rd()}]), - ?line rb:list(), - ?line Pr=one_rb_findstr([progress],"==="), - ?line Wm=one_rb_findstr([warning_msg],"==="), - ?line Wr=one_rb_findstr([warning_report],"==="), - ?line Sum=Pr+Wm+Wr, - ?line Sum=one_rb_findstr([],"UTC"), - ?line rb:stop(), - ?line application:stop(sasl), - ?line application:set_env(sasl,utc_log,UtcLog), - ?line stop_node(Node), + clean_rd(), + Node = start_node(nn(),"-boot start_sasl -sasl error_logger_mf_dir "++ + quote(rd())++" error_logger_mf_maxbytes 5000 " + "error_logger_mf_maxfiles 5 -sasl utc_log true"), + Self = self(), + GL = group_leader(), + Report = [{self,Self},{gl,GL},make_ref()], + fake_gl(Node,warning_msg,"~p~n",[Self]), + fake_gl(Node,warning_report,Report), + nice_stop_node(Node), + application:stop(sasl), + UtcLog=case application:get_env(sasl,utc_log) of + {ok,true} -> + true; + {ok,false} -> + application:set_env(sasl,utc_log,true), + false; + undefined -> + application:set_env(sasl,utc_log,true), + undefined + end, + application:start(sasl), + rb:start([{report_dir, rd()}]), + rb:list(), + Pr=one_rb_findstr([progress],"==="), + Wm=one_rb_findstr([warning_msg],"==="), + Wr=one_rb_findstr([warning_report],"==="), + Sum=Pr+Wm+Wr, + Sum=one_rb_findstr([],"UTC"), + rb:stop(), + application:stop(sasl), + case UtcLog of + undefined -> + application:unset_env(sasl,utc_log); + _ -> + application:set_env(sasl,utc_log,UtcLog) + end, + stop_node(Node), ok. file_utc() -> - ?line file:delete(lf()), - ?line SS="+W w -stdlib utc_log true -kernel error_logger "++ oquote("{file,"++iquote(lf())++"}"), - %erlang:display(SS), - ?line Node = start_node(nn(),SS), - %erlang:display(rpc:call(Node,application,get_env,[kernel,error_logger])), - ?line Self = self(), - ?line GL = group_leader(), - ?line fake_gl(Node,error_msg,"~p~n",[Self]), - ?line fake_gl(Node,warning_msg,"~p~n",[Self]), - ?line fake_gl(Node,info_msg,"~p~n",[Self]), - ?line Report = [{self,Self},{gl,GL},make_ref()], - ?line fake_gl(Node,error_report,Report), - ?line fake_gl(Node,warning_report,Report), - ?line fake_gl(Node,info_report,Report), - ?line nice_stop_node(Node), - ?line receive after 5000 -> ok end, % Let the node die, needed - ?line 6 = findstr("UTC",lf()), - ?line 2 = findstr("WARNING",lf()), - ?line 2 = findstr("ERROR",lf()), - ?line 2 = findstr("INFO",lf()), - ?line stop_node(Node), + file:delete(lf()), + SS="-stdlib utc_log true -kernel error_logger "++ oquote("{file,"++iquote(lf())++"}"), + Node = start_node(nn(),SS), + Self = self(), + GL = group_leader(), + fake_gl(Node,error_msg,"~p~n",[Self]), + fake_gl(Node,warning_msg,"~p~n",[Self]), + fake_gl(Node,info_msg,"~p~n",[Self]), + Report = [{self,Self},{gl,GL},make_ref()], + fake_gl(Node,error_report,Report), + fake_gl(Node,warning_report,Report), + fake_gl(Node,info_report,Report), + nice_stop_node(Node), + receive after 5000 -> ok end, % Let the node die, needed + 6 = findstr("UTC",lf()), + 2 = findstr("WARNING",lf()), + 2 = findstr("ERROR",lf()), + 2 = findstr("INFO",lf()), + stop_node(Node), ok. diff --git a/lib/kernel/test/file_SUITE.erl b/lib/kernel/test/file_SUITE.erl index 2ce2303ba3..e784c06865 100644 --- a/lib/kernel/test/file_SUITE.erl +++ b/lib/kernel/test/file_SUITE.erl @@ -1,23 +1,24 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2013. All Rights Reserved. +%% Copyright Ericsson AB 1996-2018. 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/. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at %% -%% 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. +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% -%% This is a developement feature when developing a new file module, +%% This is a development feature when developing a new file module, %% ugly but practical. -ifndef(FILE_MODULE). -define(FILE_MODULE, file). @@ -38,6 +39,8 @@ -define(FILE_FIN_PER_TESTCASE(Config), Config). -endif. +-define(PRIM_FILE, prim_file). + -module(?FILE_SUITE). -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, @@ -47,23 +50,24 @@ -export([cur_dir_0/1, cur_dir_1/1, make_del_dir/1, list_dir/1,list_dir_error/1, untranslatable_names/1, untranslatable_names_error/1, - pos1/1, pos2/1]). + pos1/1, pos2/1, pos3/1]). -export([close/1, consult1/1, path_consult/1, delete/1]). -export([ eval1/1, path_eval/1, script1/1, path_script/1, - open1/1, - old_modes/1, new_modes/1, path_open/1, open_errors/1]). + open1/1, + old_modes/1, new_modes/1, path_open/1, open_errors/1]). -export([ file_info_basic_file/1, file_info_basic_directory/1, - file_info_bad/1, file_info_times/1, file_write_file_info/1]). + file_info_bad/1, file_info_times/1, file_write_file_info/1, + file_wfi_helpers/1]). -export([rename/1, access/1, truncate/1, datasync/1, sync/1, read_write/1, pread_write/1, append/1, exclusive/1]). -export([ e_delete/1, e_rename/1, e_make_dir/1, e_del_dir/1]). -export([otp_5814/1, otp_10852/1]). -export([ read_not_really_compressed/1, - read_compressed_cooked/1, read_compressed_cooked_binary/1, - read_cooked_tar_problem/1, - write_compressed/1, compress_errors/1, catenated_gzips/1, - compress_async_crash/1]). + read_compressed_cooked/1, read_compressed_cooked_binary/1, + read_cooked_tar_problem/1, + write_compressed/1, compress_errors/1, catenated_gzips/1, + compress_async_crash/1]). -export([ make_link/1, read_link_info_for_non_link/1, symlinks/1]). @@ -79,9 +83,10 @@ -export([interleaved_read_write/1]). +-export([unicode/1]). -export([altname/1]). --export([large_file/1, large_write/1]). +-export([large_file/0, large_file/1, large_write/0, large_write/1]). -export([read_line_1/1, read_line_2/1, read_line_3/1,read_line_4/1]). @@ -93,6 +98,14 @@ -export([old_io_protocol/1]). +-export([unicode_mode/1]). + +-export([volume_relative_paths/1]). + +-export([tiny_writes/1, tiny_writes_delayed/1, + large_writes/1, large_writes_delayed/1, + tiny_reads/1, tiny_reads_ahead/1]). + %% Debug exports -export([create_file_slow/2, create_file/2, create_bin/2]). -export([verify_file/2, verify_bin/3]). @@ -102,21 +115,28 @@ %% System probe functions that might be handy to check from the shell -export([disc_free/1, memsize/0]). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). +-include_lib("common_test/include/ct_event.hrl"). + -include_lib("kernel/include/file.hrl"). +-define(THROW_ERROR(RES), throw({fail, ?LINE, RES})). -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap,{minutes,1}}]. all() -> - [altname, read_write_file, {group, dirs}, - {group, files}, delete, rename, names, {group, errors}, - {group, compression}, {group, links}, copy, + [unicode, altname, read_write_file, {group, dirs}, + {group, files}, delete, rename, names, volume_relative_paths, + {group, errors}, {group, compression}, {group, links}, copy, delayed_write, read_ahead, segment_read, segment_write, ipread, pid2name, interleaved_read_write, otp_5814, otp_10852, large_file, large_write, read_line_1, read_line_2, read_line_3, - read_line_4, standard_io, old_io_protocol]. + read_line_4, standard_io, old_io_protocol, + unicode_mode, {group, bench} + ]. groups() -> [{dirs, [], [make_del_dir, cur_dir_0, cur_dir_1, @@ -130,10 +150,11 @@ groups() -> [open1, old_modes, new_modes, path_open, close, access, read_write, pread_write, append, open_errors, exclusive]}, - {pos, [], [pos1, pos2]}, + {pos, [], [pos1, pos2, pos3]}, {file_info, [], [file_info_basic_file, file_info_basic_directory, - file_info_bad, file_info_times, file_write_file_info]}, + file_info_bad, file_info_times, file_write_file_info, + file_wfi_helpers]}, {consult, [], [consult1, path_consult]}, {eval, [], [eval1, path_eval]}, {script, [], [script1, path_script]}, @@ -145,11 +166,19 @@ groups() -> write_compressed, compress_errors, catenated_gzips, compress_async_crash]}, {links, [], - [make_link, read_link_info_for_non_link, symlinks]}]. + [make_link, read_link_info_for_non_link, symlinks]}, + {bench, [], + [tiny_writes, tiny_writes_delayed, + large_writes, large_writes_delayed, + tiny_reads, tiny_reads_ahead]}]. init_per_group(_GroupName, Config) -> Config. +end_per_group(bench, Config) -> + ScratchDir = proplists:get_value(priv_dir, Config), + file:delete(filename:join(ScratchDir, "benchmark_scratch_file")), + Config; end_per_group(_GroupName, Config) -> Config. @@ -161,16 +190,11 @@ init_per_suite(Config) when is_list(Config) -> ok -> [{sasl,started}] end, - ok = case os:type() of - {ose,_} -> - ok; - _ -> - application:start(os_mon) - end, + application:start(os_mon), case os:type() of {win32, _} -> - Priv = ?config(priv_dir, Config), + Priv = proplists:get_value(priv_dir, Config), HasAccessTime = case ?FILE_MODULE:read_file_info(Priv) of {ok, #file_info{atime={_, {0, 0, 0}}}} -> @@ -192,12 +216,7 @@ end_per_suite(Config) when is_list(Config) -> ok end, - case os:type() of - {ose,_} -> - ok; - _ -> - application:stop(os_mon) - end, + application:stop(os_mon), case proplists:get_value(sasl, Config) of started -> application:stop(sasl); @@ -264,73 +283,66 @@ mini_server(Parent) -> mini_server(Parent) end. -standard_io(suite) -> - []; -standard_io(doc) -> - ["Test that standard i/o-servers work with file module"]; +%% Test that standard i/o-servers work with file module. standard_io(Config) when is_list(Config) -> %% Really just a smoke test - ?line Pid = spawn(?MODULE,mini_server,[self()]), - ?line register(mini_server,Pid), - ?line ok = file:write(mini_server,<<"hej\n">>), - ?line receive - {io_request,_,_,{put_chars,<<"hej\n">>}} -> - ok - after 1000 -> - exit(noreply) - end, - ?line {ok,"aaaaa"} = file:read(mini_server,5), - ?line receive - {io_request,_,_,{get_chars,'',5}} -> - ok - after 1000 -> - exit(noreply) - end, - ?line {ok,"hej\n"} = file:read_line(mini_server), - ?line receive - {io_request,_,_,{get_line,''}} -> - ok - after 1000 -> - exit(noreply) - end, - ?line OldGL = group_leader(), - ?line group_leader(Pid,self()), - ?line ok = file:write(standard_io,<<"hej\n">>), - ?line group_leader(OldGL,self()), - ?line receive - {io_request,_,_,{put_chars,<<"hej\n">>}} -> - ok - after 1000 -> - exit(noreply) - end, - ?line group_leader(Pid,self()), - ?line {ok,"aaaaa"} = file:read(standard_io,5), - ?line group_leader(OldGL,self()), - ?line receive - {io_request,_,_,{get_chars,'',5}} -> - ok - after 1000 -> - exit(noreply) - end, - ?line group_leader(Pid,self()), - ?line {ok,"hej\n"} = file:read_line(standard_io), - ?line group_leader(OldGL,self()), - ?line receive - {io_request,_,_,{get_line,''}} -> - ok - after 1000 -> - exit(noreply) - end, + Pid = spawn(?MODULE,mini_server,[self()]), + register(mini_server,Pid), + ok = file:write(mini_server,<<"hej\n">>), + receive + {io_request,_,_,{put_chars,<<"hej\n">>}} -> + ok + after 1000 -> + exit(noreply) + end, + {ok,"aaaaa"} = file:read(mini_server,5), + receive + {io_request,_,_,{get_chars,'',5}} -> + ok + after 1000 -> + exit(noreply) + end, + {ok,"hej\n"} = file:read_line(mini_server), + receive + {io_request,_,_,{get_line,''}} -> + ok + after 1000 -> + exit(noreply) + end, + OldGL = group_leader(), + group_leader(Pid,self()), + ok = file:write(standard_io,<<"hej\n">>), + group_leader(OldGL,self()), + receive + {io_request,_,_,{put_chars,<<"hej\n">>}} -> + ok + after 1000 -> + exit(noreply) + end, + group_leader(Pid,self()), + {ok,"aaaaa"} = file:read(standard_io,5), + group_leader(OldGL,self()), + receive + {io_request,_,_,{get_chars,'',5}} -> + ok + after 1000 -> + exit(noreply) + end, + group_leader(Pid,self()), + {ok,"hej\n"} = file:read_line(standard_io), + group_leader(OldGL,self()), + receive + {io_request,_,_,{get_line,''}} -> + ok + after 1000 -> + exit(noreply) + end, Pid ! die, receive after 1000 -> ok end. -old_io_protocol(suite) -> - []; -old_io_protocol(doc) -> - ["Test that the old file IO protocol =< R16B still works"]; +%% Test that the old file IO protocol =< R16B still works. old_io_protocol(Config) when is_list(Config) -> - Dog = test_server:timetrap(test_server:seconds(5)), - RootDir = ?config(priv_dir,Config), + RootDir = proplists:get_value(priv_dir,Config), Name = filename:join(RootDir, atom_to_list(?MODULE) ++"old_io_protocol.fil"), @@ -343,113 +355,256 @@ old_io_protocol(Config) when is_list(Config) -> end, ok = ?FILE_MODULE:close(Fd), {ok, <<>>} = ?FILE_MODULE:read_file(Name), - test_server:timetrap_cancel(Dog), [] = flush(), ok. +unicode_mode(Config) -> + Dir = {dir, proplists:get_value(priv_dir,Config)}, + OptVariants = [[Dir], + [Dir, {encoding, utf8}], + [Dir, binary], + [Dir, binary, {encoding, utf8}] + ], + ReadVariants = [{read, fun(Fd) -> um_read(Fd, fun(Fd1) -> file:read(Fd1, 1024) end) end}, + {read_line, fun(Fd) -> um_read(Fd, fun(Fd1) -> file:read_line(Fd1) end) end} + %%{pread, fun(Fd) -> file:pread(Fd, 0, 1024) end}, + %%{preadl, fun(Fd) -> file:pread(Fd, [{0, 1024}]) end}, + ], + + _ = [read_write_0("ASCII: list: Hello World", Read, Opt) || + Opt <- OptVariants, Read <- ReadVariants], + _ = [read_write_0("LATIN1: list: åäöÅÄÖ", Read, Opt) || + Opt <- OptVariants, Read <- ReadVariants], + _ = [read_write_0(<<"ASCII: bin: Hello World">>, Read, Opt) || + Opt <- OptVariants, Read <- ReadVariants], + _ = [read_write_0(<<"LATIN1: bin: åäöÅÄÖ">>, Read, Opt) || + Opt <- OptVariants, Read <- ReadVariants], + %% These will be double encoded if option is encoding utf-8 + _ = [read_write_0(<<"UTF8: bin: Ωß"/utf8>>, Read, Opt) || + Opt <- OptVariants, Read <- ReadVariants], + %% These should not work (with encoding set to utf-8) + %% according to file's documentation + _ = [read_write_0("UTF8: list: Ωß", Read, Opt) || + Opt <- OptVariants, Read <- ReadVariants], + ok. + +read_write_0(Str, {Func, ReadFun}, Options) -> + try + Res = read_write_1(Str, ReadFun, Options), + io:format("~p: ~ts ~p '~p'~n", [Func, Str, tl(Options), Res]), + ok + catch {fail, Line, ReadBytes = [_|_]} -> + io:format("~p:~p: ~p ERROR: ~w vs~n ~w~n - ~p~n", + [?MODULE, Line, Func, Str, ReadBytes, Options]), + exit({error, ?LINE}); + {fail, Line, ReadBytes} -> + io:format("~p:~p: ~p ERROR: ~ts vs~n ~w~n - ~p~n", + [?MODULE, Line, Func, Str, ReadBytes, Options]), + exit({error, ?LINE}); + error:What:Stacktrace -> + io:format("~p:??: ~p ERROR: ~p from~n ~w~n ~p~n", + [?MODULE, Func, What, Str, Options]), + + io:format("\t~p~n", [Stacktrace]), + exit({error, ?LINE}) + end. + +read_write_1(Str0, ReadFun, [{dir,Dir}|Options]) -> + File = um_filename(Str0, Dir, Options), + Pre = "line 1\n", Post = "\nlast line\n", + Str = case is_list(Str0) andalso lists:max(Str0) > 255 of + false -> %% Normal case Use options + {ok, FdW} = file:open(File, [write|Options]), + IO = [Pre, Str0, Post], + ok = file:write(FdW, IO), + case is_binary(Str0) of + true -> iolist_to_binary(IO); + false -> lists:append(IO) + end; + true -> %% Test unicode lists + {ok, FdW} = file:open(File, [write]), + Utf8 = unicode:characters_to_binary([Pre, Str0, Post]), + file:write(FdW, Utf8), + {unicode, Utf8} + end, + file:close(FdW), + {ok, FdR} = file:open(File, [read|Options]), + ReadRes = ReadFun(FdR), + file:close(FdR), + Res = um_check(Str, ReadRes, Options), + file:delete(File), + Res. +um_read(Fd, Fun) -> + um_read(Fd, Fun, []). + +um_read(Fd, Fun, Acc) -> + case Fun(Fd) of + eof -> + case is_binary(hd(Acc)) of + true -> {ok, iolist_to_binary(lists:reverse(Acc))}; + false -> {ok, lists:append(lists:reverse(Acc))} + end; + {ok, Data} -> + um_read(Fd, Fun, [Data|Acc]); + Error -> + Error + end. + + +um_check(Str, {ok, Str}, _) -> ok; +um_check(Bin, {ok, Res}, _Options) when is_binary(Bin), is_list(Res) -> + case list_to_binary(Res) of + Bin -> ok; + _ -> ?THROW_ERROR(Res) + end; +um_check(Str, {ok, Res}, _Options) when is_list(Str), is_binary(Res) -> + case iolist_to_binary(Str) of + Res -> ok; + _ -> ?THROW_ERROR(Res) + end; +um_check({unicode, Utf8Bin}, Res, Options) -> + um_check_unicode(Utf8Bin, Res, + proplists:get_value(binary, Options, false), + proplists:get_value(encoding, Options, none)); +um_check(_Str, Res, _Options) -> + ?THROW_ERROR(Res). + +um_check_unicode(Utf8Bin, {ok, Utf8Bin}, true, none) -> + ok; +um_check_unicode(Utf8Bin, {ok, List = [_|_]}, false, none) -> + case binary_to_list(Utf8Bin) == List of + true -> ok; + false -> ?THROW_ERROR(List) + end; +um_check_unicode(_Utf8Bin, {error, {no_translation, unicode, latin1}}, _, _) -> + no_translation; +um_check_unicode(_Utf8Bin, Error = {error, _}, _, _Unicode) -> + ?THROW_ERROR(Error); +um_check_unicode(_Utf8Bin, {ok, _ListOrBin}, _, _UTF8_) -> + %% List = if is_binary(ListOrBin) -> unicode:characters_to_list(ListOrBin); + %% true -> ListOrBin + %% end, + %% io:format("In: ~w~n", [binary_to_list(Utf8Bin)]), + %% io:format("Ut: ~w~n", [List]), + ?THROW_ERROR({shoud_be, no_translation}). + +um_filename(Bin, Dir, Options) when is_binary(Bin) -> + um_filename(binary_to_list(Bin), Dir, Options); +um_filename(Str = [_|_], Dir, Options) -> + Name = hd(string:lexemes(Str, ":")), + Enc = atom_to_list(proplists:get_value(encoding, Options, latin1)), + File = case lists:member(binary, Options) of + true -> + "test_" ++ Name ++ "_bin_enc_" ++ Enc; + false -> + "test_" ++ Name ++ "_list_enc_" ++ Enc + end, + filename:join(Dir, File). + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -read_write_file(suite) -> []; -read_write_file(doc) -> []; read_write_file(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(5)), - ?line RootDir = ?config(priv_dir,Config), - ?line Name = filename:join(RootDir, - atom_to_list(?MODULE) - ++"_read_write_file"), + RootDir = proplists:get_value(priv_dir,Config), + Name = filename:join(RootDir, + atom_to_list(?MODULE) + ++"_read_write_file"), %% Try writing and reading back some term - ?line SomeTerm = {"This term",{will,be},[written,$t,$o],1,file,[]}, - ?line ok = ?FILE_MODULE:write_file(Name,term_to_binary(SomeTerm)), - ?line {ok,Bin1} = ?FILE_MODULE:read_file(Name), - ?line SomeTerm = binary_to_term(Bin1), - + SomeTerm = {"This term",{will,be},[written,$t,$o],1,file,[]}, + Bin1 = term_to_binary(SomeTerm), + ok = do_read_write_file(Name, Bin1), + %% Try a "null" term - ?line NullTerm = [], - ?line ok = ?FILE_MODULE:write_file(Name,term_to_binary(NullTerm)), - ?line {ok,Bin2} = ?FILE_MODULE:read_file(Name), - ?line NullTerm = binary_to_term(Bin2), - - %% Try some "complicated" types - ?line BigNum = 123456789012345678901234567890, - ?line ComplTerm = {self(),make_ref(),BigNum,3.14159}, - ?line ok = ?FILE_MODULE:write_file(Name,term_to_binary(ComplTerm)), - ?line {ok,Bin3} = ?FILE_MODULE:read_file(Name), - ?line ComplTerm = binary_to_term(Bin3), + NullTerm = [], + Bin2 = term_to_binary(NullTerm), + ok = do_read_write_file(Name, Bin2), %% Try reading a nonexistent file - ?line Name2 = filename:join(RootDir, - atom_to_list(?MODULE) - ++"_nonexistent_file"), - ?line {error, enoent} = ?FILE_MODULE:read_file(Name2), - ?line {error, enoent} = ?FILE_MODULE:read_file(""), - ?line {error, enoent} = ?FILE_MODULE:read_file(''), + Name2 = filename:join(RootDir, + atom_to_list(?MODULE) + ++"_nonexistent_file"), + {error, enoent} = ?FILE_MODULE:read_file(Name2), + {error, enoent} = ?FILE_MODULE:read_file(""), + {error, enoent} = ?FILE_MODULE:read_file(''), - % Try writing to a bad filename - ?line {error, enoent} = - ?FILE_MODULE:write_file("",term_to_binary(NullTerm)), + %% Try writing to a bad filename + {error, enoent} = do_read_write_file("", Bin2), - % Try writing something else than a binary - ?line {error, badarg} = ?FILE_MODULE:write_file(Name,{1,2,3}), - ?line {error, badarg} = ?FILE_MODULE:write_file(Name,self()), + %% Try writing something else than a binary + {error, badarg} = do_read_write_file(Name, {1,2,3}), + {error, badarg} = do_read_write_file(Name, self()), %% Some non-term binaries - ?line ok = ?FILE_MODULE:write_file(Name,[]), - ?line {ok,Bin4} = ?FILE_MODULE:read_file(Name), - ?line 0 = byte_size(Bin4), + ok = do_read_write_file(Name, []), - ?line ok = ?FILE_MODULE:write_file(Name,[Bin1,[],[[Bin2]]]), - ?line {ok,Bin5} = ?FILE_MODULE:read_file(Name), - ?line {Bin1,Bin2} = split_binary(Bin5,byte_size(Bin1)), + %% Write some iolists + ok = do_read_write_file(Name, [Bin1,[],[[Bin2]]]), + ok = do_read_write_file(Name, ["string",<<"binary">>]), + ok = do_read_write_file(Name, "pure string"), - ?line [] = flush(), - ?line test_server:timetrap_cancel(Dog), + [] = flush(), ok. +do_read_write_file(Name, Data) -> + case ?FILE_MODULE:write_file(Name, Data) of + ok -> + BinData = iolist_to_binary(Data), + {ok,BinData} = ?FILE_MODULE:read_file(Name), + + ok = ?FILE_MODULE:write_file(Name, Data, []), + {ok,BinData} = ?FILE_MODULE:read_file(Name), + + ok = ?FILE_MODULE:write_file(Name, Data, [raw]), + {ok,BinData} = ?FILE_MODULE:read_file(Name), + + ok; + {error,_}=Res -> + Res = ?FILE_MODULE:write_file(Name, Data, []), + Res = ?FILE_MODULE:write_file(Name, Data, [raw]), + Res + end. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -make_del_dir(suite) -> []; -make_del_dir(doc) -> []; make_del_dir(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(5)), - ?line RootDir = ?config(priv_dir,Config), - ?line NewDir = filename:join(RootDir, - atom_to_list(?MODULE) - ++"_mk-dir"), - ?line ok = ?FILE_MODULE:make_dir(NewDir), - ?line {error, eexist} = ?FILE_MODULE:make_dir(NewDir), - ?line ok = ?FILE_MODULE:del_dir(NewDir), - ?line {error, enoent} = ?FILE_MODULE:del_dir(NewDir), - % Make sure we are not in a directory directly under test_server - % as that would result in eacces errors when trying to delete '..', - % because there are processes having that directory as current. - ?line ok = ?FILE_MODULE:make_dir(NewDir), - ?line {ok,CurrentDir} = file:get_cwd(), + RootDir = proplists:get_value(priv_dir,Config), + NewDir = filename:join(RootDir, + atom_to_list(?MODULE) + ++"_mk-dir"), + ok = ?FILE_MODULE:make_dir(NewDir), + {error, eexist} = ?FILE_MODULE:make_dir(NewDir), + ok = ?FILE_MODULE:del_dir(NewDir), + {error, enoent} = ?FILE_MODULE:del_dir(NewDir), + %% Make sure we are not in a directory directly under test_server + %% as that would result in eacces errors when trying to delete '..', + %% because there are processes having that directory as current. + ok = ?FILE_MODULE:make_dir(NewDir), + {ok,CurrentDir} = file:get_cwd(), case {os:type(), length(NewDir) >= 260 } of {{win32,_}, true} -> io:format("Skip set_cwd for windows path longer than 260 (MAX_PATH)\n", []), io:format("\nNewDir = ~p\n", [NewDir]); _ -> - ?line ok = ?FILE_MODULE:set_cwd(NewDir) + ok = ?FILE_MODULE:set_cwd(NewDir) end, try %% Check that we get an error when trying to create... %% a deep directory - ?line NewDir2 = filename:join(RootDir, - atom_to_list(?MODULE) - ++"_mk-dir-noexist/foo"), - ?line {error, enoent} = ?FILE_MODULE:make_dir(NewDir2), + NewDir2 = filename:join(RootDir, + atom_to_list(?MODULE) + ++"_mk-dir-noexist/foo"), + {error, enoent} = ?FILE_MODULE:make_dir(NewDir2), %% a nameless directory - ?line {error, enoent} = ?FILE_MODULE:make_dir(""), + {error, enoent} = ?FILE_MODULE:make_dir(""), %% a directory with illegal name - ?line {error, badarg} = ?FILE_MODULE:make_dir({1,2,3}), - + {error, badarg} = ?FILE_MODULE:make_dir({1,2,3}), + %% a directory with illegal name, even if it's a (bad) list - ?line {error, badarg} = ?FILE_MODULE:make_dir([1,2,3,{}]), - + {error, badarg} = ?FILE_MODULE:make_dir([1,2,3,{}]), + %% Maybe this isn't an error, exactly, but worth mentioning anyway: %% ok = ?FILE_MODULE:make_dir([$f,$o,$o,0,$b,$a,$r])), %% The above line works, and created a directory "./foo" @@ -457,40 +612,36 @@ make_del_dir(Config) when is_list(Config) -> %% a directory, but with a name that incorporates the "bar" part of %% the list, so that [$f,$o,$o,0,$f,$o,$o] wouldn't refer to the same %% dir. But this would slow it down. - + %% Try deleting some bad directories %% Deleting the parent directory to the current, sounds dangerous, huh? %% Don't worry ;-) the parent directory should never be empty, right? - ?line case ?FILE_MODULE:del_dir('..') of - {error, eexist} -> ok; - {error, eacces} -> ok; %OpenBSD - {error, einval} -> ok %FreeBSD - end, - ?line {error, enoent} = ?FILE_MODULE:del_dir(""), - ?line {error, badarg} = ?FILE_MODULE:del_dir([3,2,1,{}]), - - ?line [] = flush(), - ?line test_server:timetrap_cancel(Dog) + case ?FILE_MODULE:del_dir('..') of + {error, eexist} -> ok; + {error, eacces} -> ok; %OpenBSD + {error, einval} -> ok %FreeBSD + end, + {error, enoent} = ?FILE_MODULE:del_dir(""), + {error, badarg} = ?FILE_MODULE:del_dir([3,2,1,{}]), + + [] = flush() after - ?FILE_MODULE:set_cwd(CurrentDir) + ?FILE_MODULE:set_cwd(CurrentDir) end, ok. -cur_dir_0(suite) -> []; -cur_dir_0(doc) -> []; cur_dir_0(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(5)), %% Find out the current dir, and cd to it ;-) - ?line {ok,BaseDir} = ?FILE_MODULE:get_cwd(), - ?line Dir1 = BaseDir ++ "", %% Check that it's a string - ?line ok = ?FILE_MODULE:set_cwd(Dir1), + {ok,BaseDir} = ?FILE_MODULE:get_cwd(), + Dir1 = BaseDir ++ "", %% Check that it's a string + ok = ?FILE_MODULE:set_cwd(Dir1), %% Make a new dir, and cd to that - ?line RootDir = ?config(priv_dir,Config), - ?line NewDir = filename:join(RootDir, - atom_to_list(?MODULE) - ++"_curdir"), - ?line ok = ?FILE_MODULE:make_dir(NewDir), + RootDir = proplists:get_value(priv_dir,Config), + NewDir = filename:join(RootDir, + atom_to_list(?MODULE) + ++"_curdir"), + ok = ?FILE_MODULE:make_dir(NewDir), case {os:type(), length(NewDir) >= 260} of {{win32,_}, true} -> io:format("Skip set_cwd for windows path longer than 260 (MAX_PATH):\n"), @@ -507,6 +658,10 @@ cur_dir_0(Config) when is_list(Config) -> {ok,NewDirFiles} = ?FILE_MODULE:list_dir("."), true = lists:member(UncommonName,NewDirFiles), + %% Ensure that we get the same result with a trailing slash; the + %% APIs used on Windows will choke on them if passed directly. + {ok,NewDirFiles} = ?FILE_MODULE:list_dir("./"), + %% Delete the directory and return to the old current directory %% and check that the created file isn't there (too!) expect({error, einval}, {error, eacces}, @@ -523,52 +678,51 @@ cur_dir_0(Config) when is_list(Config) -> {ok,OldDirFiles} = ?FILE_MODULE:list_dir("."), false = lists:member(UncommonName,OldDirFiles) end, - + %% Try doing some bad things - ?line {error, badarg} = ?FILE_MODULE:set_cwd({foo,bar}), - ?line {error, enoent} = ?FILE_MODULE:set_cwd(""), - ?line {error, enoent} = ?FILE_MODULE:set_cwd(".......a......"), - ?line {ok,BaseDir} = ?FILE_MODULE:get_cwd(), %% Still there? + {error, badarg} = ?FILE_MODULE:set_cwd({foo,bar}), + {error, enoent} = ?FILE_MODULE:set_cwd(""), + {error, enoent} = ?FILE_MODULE:set_cwd(".......a......"), + {ok,BaseDir} = ?FILE_MODULE:get_cwd(), %% Still there? %% On Windows, there should only be slashes, no backslashes, %% in the return value of get_cwd(). %% (The test is harmless on Unix, because filenames usually %% don't contain backslashes.) - ?line {ok, BaseDir} = ?FILE_MODULE:get_cwd(), - ?line false = lists:member($\\, BaseDir), + {ok, BaseDir} = ?FILE_MODULE:get_cwd(), + false = lists:member($\\, BaseDir), - ?line [] = flush(), - ?line test_server:timetrap_cancel(Dog), + [] = flush(), ok. %% Tests ?FILE_MODULE:get_cwd/1. -cur_dir_1(suite) -> []; -cur_dir_1(doc) -> []; cur_dir_1(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(5)), - - ?line case os:type() of - {win32, _} -> - win_cur_dir_1(Config); - _ -> - ?line {error, enotsup} = ?FILE_MODULE:get_cwd("d:") - end, - ?line [] = flush(), - ?line test_server:timetrap_cancel(Dog), + case os:type() of + {win32, _} -> + win_cur_dir_1(Config); + _ -> + {error, enotsup} = ?FILE_MODULE:get_cwd("d:") + end, + [] = flush(), ok. - + win_cur_dir_1(_Config) -> - ?line {ok,BaseDir} = ?FILE_MODULE:get_cwd(), + {ok,BaseDir} = ?FILE_MODULE:get_cwd(), %% Get the drive letter from the current directory, %% and try to get current directory for that drive. - ?line [Drive,$:|_] = BaseDir, - ?line {ok,BaseDir} = ?FILE_MODULE:get_cwd([Drive,$:]), + [CurDrive,$:|_] = BaseDir, + {ok,BaseDir} = ?FILE_MODULE:get_cwd([CurDrive,$:]), io:format("BaseDir = ~s\n", [BaseDir]), + %% We should error out on non-existent drives. Any reasonable system will + %% have at least one. + CurDirs = [?FILE_MODULE:get_cwd([Drive,$:]) || Drive <- lists:seq($A, $Z)], + lists:member({error,eaccess}, CurDirs), + %% Unfortunately, there is no way to move away from the %% current drive as we can't use the "subst" command from %% a SSH connection. We can't test any more. @@ -581,7 +735,7 @@ win_cur_dir_1(_Config) -> %%% list_dir_error(Config) -> - Priv = ?config(priv_dir, Config), + Priv = proplists:get_value(priv_dir, Config), NonExisting = filename:join(Priv, "non-existing-dir"), {error,enoent} = ?FILE_MODULE:list_dir(NonExisting), ok. @@ -591,7 +745,7 @@ list_dir_error(Config) -> %%% list_dir(Config) -> - RootDir = ?config(priv_dir, Config), + RootDir = proplists:get_value(priv_dir, Config), TestDir = filename:join(RootDir, ?MODULE_STRING++"_list_dir"), ?FILE_MODULE:make_dir(TestDir), list_dir_1(TestDir, 42, []). @@ -621,7 +775,7 @@ untranslatable_names(Config) -> untranslatable_names_1(Config) -> {ok,OldCwd} = file:get_cwd(), - PrivDir = ?config(priv_dir, Config), + PrivDir = proplists:get_value(priv_dir, Config), Dir = filename:join(PrivDir, "untranslatable_names"), ok = file:make_dir(Dir), Node = start_node(untranslatable_names, "+fnu"), @@ -662,7 +816,7 @@ untranslatable_names_error(Config) -> untranslatable_names_error_1(Config) -> {ok,OldCwd} = file:get_cwd(), - PrivDir = ?config(priv_dir, Config), + PrivDir = proplists:get_value(priv_dir, Config), Dir = filename:join(PrivDir, "untranslatable_names_error"), ok = file:make_dir(Dir), Node = start_node(untranslatable_names, "+fnue"), @@ -706,11 +860,11 @@ no_untranslatable_names() -> end. start_node(Name, Args) -> - [_,Host] = string:tokens(atom_to_list(node()), "@"), + [_,Host] = string:lexemes(atom_to_list(node()), "@"), ct:log("Trying to start ~w@~s~n", [Name,Host]), case test_server:start_node(Name, peer, [{args,Args}]) of {error,Reason} -> - test_server:fail(Reason); + ct:fail(Reason); {ok,Node} -> ct:log("Node ~p started~n", [Node]), Node @@ -721,547 +875,525 @@ start_node(Name, Args) -> -open1(suite) -> []; -open1(doc) -> []; open1(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(5)), - ?line RootDir = ?config(priv_dir,Config), - ?line NewDir = filename:join(RootDir, - atom_to_list(?MODULE) - ++"_files"), - ?line ok = ?FILE_MODULE:make_dir(NewDir), - ?line Name = filename:join(NewDir, "foo1.fil"), - ?line {ok,Fd1} = ?FILE_MODULE:open(Name,read_write), - ?line {ok,Fd2} = ?FILE_MODULE:open(Name,read), - ?line Str = "{a,tuple}.\n", - ?line io:format(Fd1,Str,[]), - ?line {ok,0} = ?FILE_MODULE:position(Fd1,bof), - ?line Str = io:get_line(Fd1,''), - ?line case io:get_line(Fd2,'') of - Str -> Str; - eof -> Str - end, - ?line ok = ?FILE_MODULE:close(Fd2), - ?line {ok,0} = ?FILE_MODULE:position(Fd1,bof), - ?line ok = ?FILE_MODULE:truncate(Fd1), - ?line eof = io:get_line(Fd1,''), - ?line ok = ?FILE_MODULE:close(Fd1), - ?line {ok,Fd3} = ?FILE_MODULE:open(Name,read), - ?line eof = io:get_line(Fd3,''), - ?line ok = ?FILE_MODULE:close(Fd3), - ?line [] = flush(), - ?line test_server:timetrap_cancel(Dog), + RootDir = proplists:get_value(priv_dir,Config), + NewDir = filename:join(RootDir, + atom_to_list(?MODULE) + ++"_files"), + ok = ?FILE_MODULE:make_dir(NewDir), + Name = filename:join(NewDir, "foo1.fil"), + {ok,Fd1} = ?FILE_MODULE:open(Name,read_write), + {ok,Fd2} = ?FILE_MODULE:open(Name,read), + Str = "{a,tuple}.\n", + io:format(Fd1,Str,[]), + {ok,0} = ?FILE_MODULE:position(Fd1,bof), + Str = io:get_line(Fd1,''), + Str = io:get_line(Fd2,''), + ok = ?FILE_MODULE:close(Fd2), + {ok,0} = ?FILE_MODULE:position(Fd1,bof), + ok = ?FILE_MODULE:truncate(Fd1), + eof = io:get_line(Fd1,''), + ok = ?FILE_MODULE:close(Fd1), + {ok,Fd3} = ?FILE_MODULE:open(Name,read), + eof = io:get_line(Fd3,''), + ok = ?FILE_MODULE:close(Fd3), + [] = flush(), ok. %% Tests all open modes. -old_modes(suite) -> []; -old_modes(doc) -> []; old_modes(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(10)), - ?line RootDir = ?config(priv_dir, Config), - ?line NewDir = filename:join(RootDir, - atom_to_list(?MODULE) - ++"_old_open_modes"), - ?line ok = ?FILE_MODULE:make_dir(NewDir), - ?line Name1 = filename:join(NewDir, "foo1.fil"), - ?line Marker = "hello, world", + RootDir = proplists:get_value(priv_dir, Config), + NewDir = filename:join(RootDir, + atom_to_list(?MODULE) + ++"_old_open_modes"), + ok = ?FILE_MODULE:make_dir(NewDir), + Name1 = filename:join(NewDir, "foo1.fil"), + Marker = "hello, world", %% write - ?line {ok, Fd1} = ?FILE_MODULE:open(Name1, write), - ?line ok = io:write(Fd1, Marker), - ?line ok = io:put_chars(Fd1, ".\n"), - ?line ok = ?FILE_MODULE:close(Fd1), + {ok, Fd1} = ?FILE_MODULE:open(Name1, write), + ok = io:write(Fd1, Marker), + ok = io:put_chars(Fd1, ".\n"), + ok = ?FILE_MODULE:close(Fd1), %% read - ?line {ok, Fd2} = ?FILE_MODULE:open(Name1, read), - ?line {ok, Marker} = io:read(Fd2, prompt), - ?line ok = ?FILE_MODULE:close(Fd2), + {ok, Fd2} = ?FILE_MODULE:open(Name1, read), + {ok, Marker} = io:read(Fd2, prompt), + ok = ?FILE_MODULE:close(Fd2), %% read_write - ?line {ok, Fd3} = ?FILE_MODULE:open(Name1, read_write), - ?line {ok, Marker} = io:read(Fd3, prompt), - ?line ok = io:write(Fd3, Marker), - ?line ok = ?FILE_MODULE:close(Fd3), + {ok, Fd3} = ?FILE_MODULE:open(Name1, read_write), + {ok, Marker} = io:read(Fd3, prompt), + ok = io:write(Fd3, Marker), + ok = ?FILE_MODULE:close(Fd3), - ?line [] = flush(), - ?line test_server:timetrap_cancel(Dog), + [] = flush(), ok. -new_modes(suite) -> []; -new_modes(doc) -> []; new_modes(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(10)), - ?line RootDir = ?config(priv_dir, Config), - ?line NewDir = filename:join(RootDir, - atom_to_list(?MODULE) - ++"_new_open_modes"), - ?line ok = ?FILE_MODULE:make_dir(NewDir), - ?line Name1 = filename:join(NewDir, "foo1.fil"), - ?line Marker = "hello, world", + RootDir = proplists:get_value(priv_dir, Config), + NewDir = filename:join(RootDir, + atom_to_list(?MODULE) + ++"_new_open_modes"), + ok = ?FILE_MODULE:make_dir(NewDir), + Name1 = filename:join(NewDir, "foo1.fil"), + Marker = "hello, world", %% write - ?line {ok, Fd1} = ?FILE_MODULE:open(Name1, [write]), - ?line ok = io:write(Fd1, Marker), - ?line ok = io:put_chars(Fd1, ".\n"), - ?line ok = ?FILE_MODULE:close(Fd1), + {ok, Fd1} = ?FILE_MODULE:open(Name1, [write]), + ok = io:write(Fd1, Marker), + ok = io:put_chars(Fd1, ".\n"), + ok = ?FILE_MODULE:close(Fd1), %% read - ?line {ok, Fd2} = ?FILE_MODULE:open(Name1, [read]), - ?line {ok, Marker} = io:read(Fd2, prompt), - ?line ok = ?FILE_MODULE:close(Fd2), + {ok, Fd2} = ?FILE_MODULE:open(Name1, [read]), + {ok, Marker} = io:read(Fd2, prompt), + ok = ?FILE_MODULE:close(Fd2), %% read and write - ?line {ok, Fd3} = ?FILE_MODULE:open(Name1, [read, write]), - ?line {ok, Marker} = io:read(Fd3, prompt), - ?line ok = io:write(Fd3, Marker), - ?line ok = ?FILE_MODULE:close(Fd3), + {ok, Fd3} = ?FILE_MODULE:open(Name1, [read, write]), + {ok, Marker} = io:read(Fd3, prompt), + ok = io:write(Fd3, Marker), + ok = ?FILE_MODULE:close(Fd3), %% read by default - ?line {ok, Fd4} = ?FILE_MODULE:open(Name1, []), - ?line {ok, Marker} = io:read(Fd4, prompt), - ?line ok = ?FILE_MODULE:close(Fd4), + {ok, Fd4} = ?FILE_MODULE:open(Name1, []), + {ok, Marker} = io:read(Fd4, prompt), + ok = ?FILE_MODULE:close(Fd4), %% read and binary - ?line {ok, Fd5} = ?FILE_MODULE:open(Name1, [read, binary]), - ?line {ok, Marker} = io:read(Fd5, prompt), - ?line ok = ?FILE_MODULE:close(Fd5), + {ok, Fd5} = ?FILE_MODULE:open(Name1, [read, binary]), + {ok, Marker} = io:read(Fd5, prompt), + ok = ?FILE_MODULE:close(Fd5), %% read, raw - ?line {ok, Fd6} = ?FILE_MODULE:open(Name1, [read, raw]), - ?line {ok, [$\[]} = ?FILE_MODULE:read(Fd6, 1), - ?line ok = ?FILE_MODULE:close(Fd6), - - %% write and sync - case ?FILE_MODULE:open(Name1, [write, sync]) of - {ok, Fd7} -> - ok = io:write(Fd7, Marker), - ok = io:put_chars(Fd7, ".\n"), - ok = ?FILE_MODULE:close(Fd7), - {ok, Fd8} = ?FILE_MODULE:open(Name1, [read]), - {ok, Marker} = io:read(Fd8, prompt), - ok = ?FILE_MODULE:close(Fd8); - {error, enotsup} -> - %% for platforms that don't support the sync option - ok - end, - - ?line [] = flush(), - ?line test_server:timetrap_cancel(Dog), - ok. + {ok, Fd6} = ?FILE_MODULE:open(Name1, [read, raw]), + {ok, [$\[]} = ?FILE_MODULE:read(Fd6, 1), + ok = ?FILE_MODULE:close(Fd6), + + %% write and sync + case ?FILE_MODULE:open(Name1, [write, sync]) of + {ok, Fd7} -> + ok = io:write(Fd7, Marker), + ok = io:put_chars(Fd7, ".\n"), + ok = ?FILE_MODULE:close(Fd7), + {ok, Fd8} = ?FILE_MODULE:open(Name1, [read]), + {ok, Marker} = io:read(Fd8, prompt), + ok = ?FILE_MODULE:close(Fd8); + {error, enotsup} -> + %% for platforms that don't support the sync option + ok + end, + + [] = flush(), + ok. -path_open(suite) -> []; -path_open(doc) -> []; path_open(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(5)), - ?line RootDir = ?config(priv_dir,Config), - ?line NewDir = filename:join(RootDir, - atom_to_list(?MODULE) - ++"_path_open"), - ?line ok = ?FILE_MODULE:make_dir(NewDir), - ?line FileName = "path_open.fil", - ?line Name = filename:join(RootDir, FileName), - ?line {ok,Fd1,_FullName1} = + RootDir = proplists:get_value(priv_dir,Config), + NewDir = filename:join(RootDir, + atom_to_list(?MODULE) + ++"_path_open"), + ok = ?FILE_MODULE:make_dir(NewDir), + FileName = "path_open.fil", + Name = filename:join(RootDir, FileName), + {ok,Fd1,_FullName1} = ?FILE_MODULE:path_open( - [RootDir, - "nosuch1", - NewDir],FileName,write), - ?line io:format(Fd1,"ABCDEFGH",[]), - ?line ok = ?FILE_MODULE:close(Fd1), + [RootDir, + "nosuch1", + NewDir],FileName,write), + io:format(Fd1,"ABCDEFGH",[]), + ok = ?FILE_MODULE:close(Fd1), %% locate it in the last dir - ?line {ok,Fd2,_FullName2} = + {ok,Fd2,_FullName2} = ?FILE_MODULE:path_open( - ["nosuch1", - NewDir, - RootDir],FileName,read), - ?line {ok,2} = + ["nosuch1", + NewDir, + RootDir],FileName,read), + {ok,2} = ?FILE_MODULE:position(Fd2,2), "C" = io:get_chars(Fd2,'',1), - ?line ok = ?FILE_MODULE:close(Fd2), + ok = ?FILE_MODULE:close(Fd2), %% Try a failing path - ?line {error, enoent} = ?FILE_MODULE:path_open( - ["nosuch1", - NewDir],FileName,read), + {error, enoent} = ?FILE_MODULE:path_open( + ["nosuch1", + NewDir],FileName,read), %% Check that it's found regardless of path, if an absolute name given - ?line {ok,Fd3,_FullPath3} = + {ok,Fd3,_FullPath3} = ?FILE_MODULE:path_open( - ["nosuch1", - NewDir],Name,read), - ?line {ok,2} = + ["nosuch1", + NewDir],Name,read), + {ok,2} = ?FILE_MODULE:position(Fd3,2), "C" = io:get_chars(Fd3,'',1), - ?line ok = ?FILE_MODULE:close(Fd3), + ok = ?FILE_MODULE:close(Fd3), - ?line [] = flush(), - ?line test_server:timetrap_cancel(Dog), + [] = flush(), ok. -close(suite) -> []; -close(doc) -> []; close(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(5)), - ?line RootDir = ?config(priv_dir,Config), - ?line Name = filename:join(RootDir, - atom_to_list(?MODULE) - ++"_close.fil"), - ?line {ok,Fd1} = ?FILE_MODULE:open(Name,read_write), + RootDir = proplists:get_value(priv_dir,Config), + Name = filename:join(RootDir, + atom_to_list(?MODULE) + ++"_close.fil"), + {ok,Fd1} = ?FILE_MODULE:open(Name,read_write), %% Just closing it is no fun, we did that a million times already %% This is a common error, for code written before Erlang 4.3 %% bacause then ?FILE_MODULE:open just returned a Pid, and not everyone %% really checked what they got. - ?line {'EXIT',_Msg} = (catch ok = ?FILE_MODULE:close({ok,Fd1})), - ?line ok = ?FILE_MODULE:close(Fd1), + {'EXIT',_Msg} = (catch ok = ?FILE_MODULE:close({ok,Fd1})), + ok = ?FILE_MODULE:close(Fd1), %% Try closing one more time - ?line Val = ?FILE_MODULE:close(Fd1), - ?line io:format("Second close gave: ~p",[Val]), + Val = ?FILE_MODULE:close(Fd1), + io:format("Second close gave: ~p",[Val]), - ?line [] = flush(), - ?line test_server:timetrap_cancel(Dog), + %% All operations on a closed raw file should EINVAL, even if they're not + %% supported on the current platform. + {ok,Fd2} = ?FILE_MODULE:open(Name, [read, write, raw]), + ok = ?FILE_MODULE:close(Fd2), + + {error, einval} = ?FILE_MODULE:advise(Fd2, 5, 5, normal), + {error, einval} = ?FILE_MODULE:allocate(Fd2, 5, 5), + {error, einval} = ?FILE_MODULE:close(Fd2), + {error, einval} = ?FILE_MODULE:datasync(Fd2), + {error, einval} = ?FILE_MODULE:position(Fd2, 5), + {error, einval} = ?FILE_MODULE:pread(Fd2, 5, 1), + {error, einval} = ?FILE_MODULE:pwrite(Fd2, 5, "einval please"), + {error, einval} = ?FILE_MODULE:read(Fd2, 1), + {error, einval} = ?FILE_MODULE:sync(Fd2), + {error, einval} = ?FILE_MODULE:truncate(Fd2), + {error, einval} = ?FILE_MODULE:write(Fd2, "einval please"), + + [] = flush(), ok. -access(suite) -> []; -access(doc) -> []; access(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(5)), - ?line RootDir = ?config(priv_dir,Config), - ?line Name = filename:join(RootDir, - atom_to_list(?MODULE) - ++"_access.fil"), - ?line Str = "ABCDEFGH", - ?line {ok,Fd1} = ?FILE_MODULE:open(Name,write), - ?line io:format(Fd1,Str,[]), - ?line ok = ?FILE_MODULE:close(Fd1), + RootDir = proplists:get_value(priv_dir,Config), + Name = filename:join(RootDir, + atom_to_list(?MODULE) + ++"_access.fil"), + Str = "ABCDEFGH", + {ok,Fd1} = ?FILE_MODULE:open(Name,write), + io:format(Fd1,Str,[]), + ok = ?FILE_MODULE:close(Fd1), %% Check that we can't write when in read only mode - ?line {ok,Fd2} = ?FILE_MODULE:open(Name,read), - ?line case catch io:format(Fd2,"XXXX",[]) of - ok -> - test_server:fail({format,write}); - _ -> - ok - end, - ?line ok = ?FILE_MODULE:close(Fd2), - ?line {ok,Fd3} = ?FILE_MODULE:open(Name,read), - ?line Str = io:get_line(Fd3,''), - ?line ok = ?FILE_MODULE:close(Fd3), + {ok,Fd2} = ?FILE_MODULE:open(Name,read), + case catch io:format(Fd2,"XXXX",[]) of + ok -> + ct:fail({format,write}); + _ -> + ok + end, + ok = ?FILE_MODULE:close(Fd2), + {ok,Fd3} = ?FILE_MODULE:open(Name,read), + Str = io:get_line(Fd3,''), + ok = ?FILE_MODULE:close(Fd3), - ?line [] = flush(), - ?line test_server:timetrap_cancel(Dog), + [] = flush(), ok. %% Tests ?FILE_MODULE:read/2 and ?FILE_MODULE:write/2. -read_write(suite) -> []; -read_write(doc) -> []; read_write(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(5)), - ?line RootDir = ?config(priv_dir, Config), - ?line NewDir = filename:join(RootDir, - atom_to_list(?MODULE) - ++"_read_write"), - ?line ok = ?FILE_MODULE:make_dir(NewDir), - ?line Marker = "hello, world", - ?line MarkerB = list_to_binary(Marker), + RootDir = proplists:get_value(priv_dir, Config), + NewDir = filename:join(RootDir, + atom_to_list(?MODULE) + ++"_read_write"), + ok = ?FILE_MODULE:make_dir(NewDir), + Marker = "hello, world", + MarkerB = list_to_binary(Marker), %% Plain file. - ?line Name1 = filename:join(NewDir, "plain.fil"), - ?line {ok, Fd1} = ?FILE_MODULE:open(Name1, [read, write]), - ?line read_write_test(Fd1, Marker, []), + Name1 = filename:join(NewDir, "plain.fil"), + {ok, Fd1} = ?FILE_MODULE:open(Name1, [read, write]), + read_write_test(Fd1, Marker, []), %% Raw file. - ?line Name2 = filename:join(NewDir, "raw.fil"), - ?line {ok, Fd2} = ?FILE_MODULE:open(Name2, [read, write, raw]), - ?line read_write_test(Fd2, Marker, []), + Name2 = filename:join(NewDir, "raw.fil"), + {ok, Fd2} = ?FILE_MODULE:open(Name2, [read, write, raw]), + read_write_test(Fd2, Marker, []), %% Plain binary file. - ?line Name3 = filename:join(NewDir, "plain-b.fil"), - ?line {ok, Fd3} = ?FILE_MODULE:open(Name3, [read, write, binary]), - ?line read_write_test(Fd3, MarkerB, <<>>), + Name3 = filename:join(NewDir, "plain-b.fil"), + {ok, Fd3} = ?FILE_MODULE:open(Name3, [read, write, binary]), + read_write_test(Fd3, MarkerB, <<>>), %% Raw binary file. - ?line Name4 = filename:join(NewDir, "raw-b.fil"), - ?line {ok, Fd4} = ?FILE_MODULE:open(Name4, [read, write, raw, binary]), - ?line read_write_test(Fd4, MarkerB, <<>>), + Name4 = filename:join(NewDir, "raw-b.fil"), + {ok, Fd4} = ?FILE_MODULE:open(Name4, [read, write, raw, binary]), + read_write_test(Fd4, MarkerB, <<>>), - ?line test_server:timetrap_cancel(Dog), ok. read_write_test(File, Marker, Empty) -> - ?line ok = ?FILE_MODULE:write(File, Marker), - ?line {ok, 0} = ?FILE_MODULE:position(File, 0), - ?line {ok, Empty} = ?FILE_MODULE:read(File, 0), - ?line {ok, Marker} = ?FILE_MODULE:read(File, 100), - ?line eof = ?FILE_MODULE:read(File, 100), - ?line {ok, Empty} = ?FILE_MODULE:read(File, 0), - ?line ok = ?FILE_MODULE:close(File), - ?line [] = flush(), + ok = ?FILE_MODULE:write(File, Marker), + {ok, 0} = ?FILE_MODULE:position(File, 0), + {ok, Empty} = ?FILE_MODULE:read(File, 0), + {ok, Marker} = ?FILE_MODULE:read(File, 100), + eof = ?FILE_MODULE:read(File, 100), + {ok, Empty} = ?FILE_MODULE:read(File, 0), + ok = ?FILE_MODULE:close(File), + [] = flush(), ok. %% Tests ?FILE_MODULE:pread/2 and ?FILE_MODULE:pwrite/2. -pread_write(suite) -> []; -pread_write(doc) -> []; pread_write(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(5)), - ?line RootDir = ?config(priv_dir, Config), - ?line NewDir = filename:join(RootDir, - atom_to_list(?MODULE) - ++"_pread_write"), - ?line ok = ?FILE_MODULE:make_dir(NewDir), - ?line List = "hello, world", - ?line Bin = list_to_binary(List), + RootDir = proplists:get_value(priv_dir, Config), + NewDir = filename:join(RootDir, + atom_to_list(?MODULE) + ++"_pread_write"), + ok = ?FILE_MODULE:make_dir(NewDir), + List = "hello, world", + Bin = list_to_binary(List), %% Plain file. - ?line Name1 = filename:join(NewDir, "plain.fil"), - ?line {ok, Fd1} = ?FILE_MODULE:open(Name1, [read, write]), - ?line pread_write_test(Fd1, List), + Name1 = filename:join(NewDir, "plain.fil"), + {ok, Fd1} = ?FILE_MODULE:open(Name1, [read, write]), + pread_write_test(Fd1, List), %% Raw file. - ?line Name2 = filename:join(NewDir, "raw.fil"), - ?line {ok, Fd2} = ?FILE_MODULE:open(Name2, [read, write, raw]), - ?line pread_write_test(Fd2, List), + Name2 = filename:join(NewDir, "raw.fil"), + {ok, Fd2} = ?FILE_MODULE:open(Name2, [read, write, raw]), + pread_write_test(Fd2, List), %% Plain file. Binary mode. - ?line Name3 = filename:join(NewDir, "plain-binary.fil"), - ?line {ok, Fd3} = ?FILE_MODULE:open(Name3, [binary, read, write]), - ?line pread_write_test(Fd3, Bin), + Name3 = filename:join(NewDir, "plain-binary.fil"), + {ok, Fd3} = ?FILE_MODULE:open(Name3, [binary, read, write]), + pread_write_test(Fd3, Bin), %% Raw file. Binary mode. - ?line Name4 = filename:join(NewDir, "raw-binary.fil"), - ?line {ok, Fd4} = ?FILE_MODULE:open(Name4, [binary, read, write, raw]), - ?line pread_write_test(Fd4, Bin), + Name4 = filename:join(NewDir, "raw-binary.fil"), + {ok, Fd4} = ?FILE_MODULE:open(Name4, [binary, read, write, raw]), + pread_write_test(Fd4, Bin), - ?line test_server:timetrap_cancel(Dog), ok. pread_write_test(File, Data) -> - ?line io:format("~p:pread_write_test(~p,~p)~n", [?MODULE, File, Data]), - ?line Size = if is_binary(Data) -> byte_size(Data); - is_list(Data) -> length(Data) - end, - ?line I = Size + 17, - ?line ok = ?FILE_MODULE:pwrite(File, 0, Data), - Res = ?FILE_MODULE:pread(File, 0, I), - ?line {ok, Data} = Res, - ?line eof = ?FILE_MODULE:pread(File, I, 1), - ?line ok = ?FILE_MODULE:pwrite(File, [{0, Data}, {I, Data}]), - ?line {ok, [Data, eof, Data]} = + io:format("~p:pread_write_test(~p,~p)~n", [?MODULE, File, Data]), + Size = if is_binary(Data) -> byte_size(Data); + is_list(Data) -> length(Data) + end, + I = Size + 17, + ok = ?FILE_MODULE:pwrite(File, 0, Data), + {ok, Data} = ?FILE_MODULE:pread(File, 0, I), + {ok, [Data]} = ?FILE_MODULE:pread(File, [{0, I}]), + eof = ?FILE_MODULE:pread(File, I, 1), + ok = ?FILE_MODULE:pwrite(File, [{0, Data}, {I, Data}]), + {ok, [Data, eof, Data]} = ?FILE_MODULE:pread(File, [{0, Size}, {2*I, 1}, {I, Size}]), - ?line Plist = lists:seq(21*I, 0, -I), - ?line Pwrite = lists:map(fun(P)->{P,Data}end, Plist), - ?line Pread = [{22*I,Size} | lists:map(fun(P)->{P,Size}end, Plist)], - ?line Presult = [eof | lists:map(fun(_)->Data end, Plist)], - ?line ok = ?FILE_MODULE:pwrite(File, Pwrite), - ?line {ok, Presult} = ?FILE_MODULE:pread(File, Pread), - ?line ok = ?FILE_MODULE:close(File), - ?line [] = flush(), + Plist = lists:seq(21*I, 0, -I), + Pwrite = lists:map(fun(P)->{P,Data}end, Plist), + Pread = [{22*I,Size} | lists:map(fun(P)->{P,Size}end, Plist)], + Presult = [eof | lists:map(fun(_)->Data end, Plist)], + ok = ?FILE_MODULE:pwrite(File, Pwrite), + {ok, Presult} = ?FILE_MODULE:pread(File, Pread), + ok = ?FILE_MODULE:close(File), + [] = flush(), ok. -append(doc) -> "Test appending to a file."; -append(suite) -> []; +%% Test appending to a file. append(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(5)), - ?line RootDir = ?config(priv_dir, Config), - ?line NewDir = filename:join(RootDir, - atom_to_list(?MODULE) - ++"_append"), - ?line ok = ?FILE_MODULE:make_dir(NewDir), + RootDir = proplists:get_value(priv_dir, Config), + NewDir = filename:join(RootDir, + atom_to_list(?MODULE) + ++"_append"), + ok = ?FILE_MODULE:make_dir(NewDir), - ?line First = "First line\n", - ?line Second = "Seond lines comes here\n", - ?line Third = "And here is the third line\n", + First = "First line\n", + Second = "Seond lines comes here\n", + Third = "And here is the third line\n", %% Write a small text file. - ?line Name1 = filename:join(NewDir, "a_file.txt"), - ?line {ok, Fd1} = ?FILE_MODULE:open(Name1, [write]), - ?line ok = io:format(Fd1, First, []), - ?line ok = io:format(Fd1, Second, []), - ?line ok = ?FILE_MODULE:close(Fd1), + Name1 = filename:join(NewDir, "a_file.txt"), + {ok, Fd1} = ?FILE_MODULE:open(Name1, [write]), + ok = io:format(Fd1, First, []), + ok = io:format(Fd1, Second, []), + ok = ?FILE_MODULE:close(Fd1), %% Open it a again and a append a line to it. - ?line {ok, Fd2} = ?FILE_MODULE:open(Name1, [append]), - ?line ok = io:format(Fd2, Third, []), - ?line ok = ?FILE_MODULE:close(Fd2), + {ok, Fd2} = ?FILE_MODULE:open(Name1, [append]), + ok = io:format(Fd2, Third, []), + ok = ?FILE_MODULE:close(Fd2), %% Read it back and verify. - ?line Expected = list_to_binary([First, Second, Third]), - ?line {ok, Expected} = ?FILE_MODULE:read_file(Name1), + Expected = list_to_binary([First, Second, Third]), + {ok, Expected} = ?FILE_MODULE:read_file(Name1), - ?line [] = flush(), - ?line test_server:timetrap_cancel(Dog), + [] = flush(), ok. -open_errors(suite) -> []; -open_errors(doc) -> []; open_errors(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(5)), - ?line DataDir = + DataDir = filename:dirname( - filename:join(?config(data_dir, Config), "x")), - ?line DataDirSlash = DataDir++"/", - ?line {error, E1} = ?FILE_MODULE:open(DataDir, [read]), - ?line {error, E2} = ?FILE_MODULE:open(DataDirSlash, [read]), - ?line {error, E3} = ?FILE_MODULE:open(DataDir, [write]), - ?line {error, E4} = ?FILE_MODULE:open(DataDirSlash, [write]), - ?line {eisdir,eisdir,eisdir,eisdir} = {E1,E2,E3,E4}, - - ?line [] = flush(), - ?line test_server:timetrap_cancel(Dog), + filename:join(proplists:get_value(data_dir, Config), "x")), + DataDirSlash = DataDir++"/", + {error, E1} = ?FILE_MODULE:open(DataDir, [read]), + {error, E2} = ?FILE_MODULE:open(DataDirSlash, [read]), + {error, E3} = ?FILE_MODULE:open(DataDir, [write]), + {error, E4} = ?FILE_MODULE:open(DataDirSlash, [write]), + {eisdir,eisdir,eisdir,eisdir} = {E1,E2,E3,E4}, + + [] = flush(), ok. -exclusive(suite) -> []; -exclusive(doc) -> "Test exclusive access to a file."; +%% Test exclusive access to a file. exclusive(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(5)), - ?line RootDir = ?config(priv_dir,Config), - ?line NewDir = filename:join(RootDir, - atom_to_list(?MODULE) - ++"_exclusive"), - ?line ok = ?FILE_MODULE:make_dir(NewDir), - ?line Name = filename:join(NewDir, "ex_file.txt"), - ?line {ok, Fd} = ?FILE_MODULE:open(Name, [write, exclusive]), - ?line {error, eexist} = ?FILE_MODULE:open(Name, [write, exclusive]), - ?line ok = ?FILE_MODULE:close(Fd), - ?line test_server:timetrap_cancel(Dog), + RootDir = proplists:get_value(priv_dir,Config), + NewDir = filename:join(RootDir, + atom_to_list(?MODULE) + ++"_exclusive"), + ok = ?FILE_MODULE:make_dir(NewDir), + Name = filename:join(NewDir, "ex_file.txt"), + {ok, Fd} = ?FILE_MODULE:open(Name, [write, exclusive]), + {error, eexist} = ?FILE_MODULE:open(Name, [write, exclusive]), + ok = ?FILE_MODULE:close(Fd), ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -pos1(suite) -> []; -pos1(doc) -> []; pos1(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(5)), - ?line RootDir = ?config(priv_dir,Config), - ?line Name = filename:join(RootDir, - atom_to_list(?MODULE) - ++"_pos1.fil"), - ?line {ok,Fd1} = ?FILE_MODULE:open(Name,write), - ?line io:format(Fd1,"ABCDEFGH",[]), - ?line ok = ?FILE_MODULE:close(Fd1), - ?line {ok,Fd2} = ?FILE_MODULE:open(Name,read), + RootDir = proplists:get_value(priv_dir,Config), + Name = filename:join(RootDir, + atom_to_list(?MODULE) + ++"_pos1.fil"), + {ok,Fd1} = ?FILE_MODULE:open(Name,write), + io:format(Fd1,"ABCDEFGH",[]), + ok = ?FILE_MODULE:close(Fd1), + {ok,Fd2} = ?FILE_MODULE:open(Name,read), %% Start pos is first char - ?line io:format("Relative positions"), - ?line "A" = io:get_chars(Fd2,'',1), - ?line {ok,2} = ?FILE_MODULE:position(Fd2,{cur,1}), - ?line "C" = io:get_chars(Fd2,'',1), - ?line {ok,0} = ?FILE_MODULE:position(Fd2,{cur,-3}), - ?line "A" = io:get_chars(Fd2,'',1), + io:format("Relative positions"), + "A" = io:get_chars(Fd2,'',1), + {ok,2} = ?FILE_MODULE:position(Fd2,{cur,1}), + "C" = io:get_chars(Fd2,'',1), + {ok,0} = ?FILE_MODULE:position(Fd2,{cur,-3}), + "A" = io:get_chars(Fd2,'',1), %% Backwards from first char should be an error - ?line {ok,0} = ?FILE_MODULE:position(Fd2,{cur,-1}), - ?line {error, einval} = ?FILE_MODULE:position(Fd2,{cur,-1}), + {ok,0} = ?FILE_MODULE:position(Fd2,{cur,-1}), + {error, einval} = ?FILE_MODULE:position(Fd2,{cur,-1}), %% Reset position and move again - ?line {ok,0} = ?FILE_MODULE:position(Fd2,0), - ?line {ok,2} = ?FILE_MODULE:position(Fd2,{cur,2}), - ?line "C" = io:get_chars(Fd2,'',1), + {ok,0} = ?FILE_MODULE:position(Fd2,0), + {ok,2} = ?FILE_MODULE:position(Fd2,{cur,2}), + "C" = io:get_chars(Fd2,'',1), %% Go a lot forwards - ?line {ok,13} = ?FILE_MODULE:position(Fd2,{cur,10}), - ?line eof = io:get_chars(Fd2,'',1), + {ok,13} = ?FILE_MODULE:position(Fd2,{cur,10}), + eof = io:get_chars(Fd2,'',1), %% Try some fixed positions - ?line io:format("Fixed positions"), - ?line {ok,8} = + io:format("Fixed positions"), + {ok,8} = ?FILE_MODULE:position(Fd2,8), eof = io:get_chars(Fd2,'',1), - ?line {ok,8} = + {ok,8} = ?FILE_MODULE:position(Fd2,cur), eof = io:get_chars(Fd2,'',1), - ?line {ok,7} = + {ok,7} = ?FILE_MODULE:position(Fd2,7), "H" = io:get_chars(Fd2,'',1), - ?line {ok,0} = + {ok,0} = ?FILE_MODULE:position(Fd2,0), "A" = io:get_chars(Fd2,'',1), - ?line {ok,3} = + {ok,3} = ?FILE_MODULE:position(Fd2,3), "D" = io:get_chars(Fd2,'',1), - ?line {ok,12} = + {ok,12} = ?FILE_MODULE:position(Fd2,12), eof = io:get_chars(Fd2,'',1), - ?line {ok,3} = + {ok,3} = ?FILE_MODULE:position(Fd2,3), "D" = io:get_chars(Fd2,'',1), %% Try the {bof,X} notation - ?line {ok,3} = ?FILE_MODULE:position(Fd2,{bof,3}), - ?line "D" = io:get_chars(Fd2,'',1), + {ok,3} = ?FILE_MODULE:position(Fd2,{bof,3}), + "D" = io:get_chars(Fd2,'',1), %% Try eof positions - ?line io:format("EOF positions"), - ?line {ok,8} = + io:format("EOF positions"), + {ok,8} = ?FILE_MODULE:position(Fd2,{eof,0}), eof=io:get_chars(Fd2,'',1), - ?line {ok,7} = + {ok,7} = ?FILE_MODULE:position(Fd2,{eof,-1}), - ?line "H" = io:get_chars(Fd2,'',1), - ?line {ok,0} = + "H" = io:get_chars(Fd2,'',1), + {ok,0} = ?FILE_MODULE:position(Fd2,{eof,-8}), "A"=io:get_chars(Fd2,'',1), - ?line {error, einval} = ?FILE_MODULE:position(Fd2,{eof,-9}), + {error, einval} = ?FILE_MODULE:position(Fd2,{eof,-9}), - ?line [] = flush(), - ?line test_server:timetrap_cancel(Dog), + [] = flush(), ok. -pos2(suite) -> []; -pos2(doc) -> []; pos2(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(5)), - ?line RootDir = ?config(priv_dir,Config), - ?line Name = filename:join(RootDir, - atom_to_list(?MODULE) - ++"_pos2.fil"), - ?line {ok,Fd1} = ?FILE_MODULE:open(Name,write), - ?line io:format(Fd1,"ABCDEFGH",[]), - ?line ok = ?FILE_MODULE:close(Fd1), - ?line {ok,Fd2} = ?FILE_MODULE:open(Name,read), - ?line {error, einval} = ?FILE_MODULE:position(Fd2,-1), + RootDir = proplists:get_value(priv_dir,Config), + Name = filename:join(RootDir, + atom_to_list(?MODULE) + ++"_pos2.fil"), + {ok,Fd1} = ?FILE_MODULE:open(Name,write), + io:format(Fd1,"ABCDEFGH",[]), + ok = ?FILE_MODULE:close(Fd1), + {ok,Fd2} = ?FILE_MODULE:open(Name,read), + {error, einval} = ?FILE_MODULE:position(Fd2,-1), %% Make sure that we still can search after an error. - ?line {ok,0} = ?FILE_MODULE:position(Fd2, 0), - ?line {ok,3} = ?FILE_MODULE:position(Fd2, {bof,3}), - ?line "D" = io:get_chars(Fd2,'',1), + {ok,0} = ?FILE_MODULE:position(Fd2, 0), + {ok,3} = ?FILE_MODULE:position(Fd2, {bof,3}), + "D" = io:get_chars(Fd2,'',1), - ?line [] = flush(), - ?line io:format("DONE"), - ?line test_server:timetrap_cancel(Dog), + [] = flush(), + io:format("DONE"), ok. +%% When it does not use raw mode, file:position had a bug. +pos3(Config) when is_list(Config) -> + RootDir = proplists:get_value(data_dir, Config), + Name = filename:join(RootDir, "realmen.html.gz"), + + {ok, Fd} = ?FILE_MODULE:open(Name, [read, binary]), + {ok, _} = ?FILE_MODULE:read(Fd, 5), + {error, einval} = ?FILE_MODULE:position(Fd, {bof, -1}), + + %% Here ok had returned =( + {error, einval} = ?FILE_MODULE:position(Fd, {cur, -10}), + %% That test is actually questionable since file:position/2 + %% is documented to leave the file position undefined after + %% it has returned an error. But on Posix systems the position + %% is guaranteed to be unchanged after an error return. On e.g + %% Windows there is nothing stated about this in the documentation. + + ok. -file_info_basic_file(suite) -> []; -file_info_basic_file(doc) -> []; file_info_basic_file(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(5)), - ?line RootDir = ?config(priv_dir, Config), + RootDir = proplists:get_value(priv_dir, Config), %% Create a short file. - ?line Name = filename:join(RootDir, - atom_to_list(?MODULE) - ++"_basic_test.fil"), - ?line {ok,Fd1} = ?FILE_MODULE:open(Name, write), - ?line io:put_chars(Fd1, "foo bar"), - ?line ok = ?FILE_MODULE:close(Fd1), + Name = filename:join(RootDir, + atom_to_list(?MODULE) + ++"_basic_test.fil"), + {ok,Fd1} = ?FILE_MODULE:open(Name, write), + io:put_chars(Fd1, "foo bar"), + ok = ?FILE_MODULE:close(Fd1), + + %% Don't crash the file server when passing incorrect arguments. + {error,badarg} = ?FILE_MODULE:read_file_info(Name, [{time, gurka}]), + {error,badarg} = ?FILE_MODULE:read_file_info([#{} | gaffel]), %% Test that the file has the expected attributes. %% The times are tricky, so we will save them to a separate test case. {ok,FileInfo} = ?FILE_MODULE:read_file_info(Name), {ok,FileInfo} = ?FILE_MODULE:read_file_info(Name, [raw]), #file_info{size=Size,type=Type,access=Access, - atime=AccessTime,mtime=ModifyTime} = FileInfo, - ?line io:format("Access ~p, Modify ~p", [AccessTime, ModifyTime]), - ?line Size = 7, - ?line Type = regular, - ?line read_write = Access, - ?line true = abs(time_dist(filter_atime(AccessTime, Config), - filter_atime(ModifyTime, - Config))) < 2, - ?line all_integers(tuple_to_list(AccessTime) ++ tuple_to_list(ModifyTime)), - - ?line [] = flush(), - ?line test_server:timetrap_cancel(Dog), + atime=AccessTime,mtime=ModifyTime} = FileInfo, + io:format("Access ~p, Modify ~p", [AccessTime, ModifyTime]), + Size = 7, + Type = regular, + read_write = Access, + true = abs(time_dist(filter_atime(AccessTime, Config), + filter_atime(ModifyTime, + Config))) < 2, + all_integers(tuple_to_list(AccessTime) ++ tuple_to_list(ModifyTime)), + + [] = flush(), ok. -file_info_basic_directory(suite) -> []; -file_info_basic_directory(doc) -> []; file_info_basic_directory(Config) when is_list(Config) -> - Dog = test_server:timetrap(test_server:seconds(5)), - %% Note: filename:join/1 removes any trailing slash, %% which is essential for ?FILE_MODULE:file_info/1 to work on %% platforms such as Windows95. - RootDir = filename:join([?config(priv_dir, Config)]), + RootDir = filename:join([proplists:get_value(priv_dir, Config)]), %% Test that the RootDir directory has the expected attributes. test_directory(RootDir, read_write), @@ -1272,65 +1404,57 @@ file_info_basic_directory(Config) when is_list(Config) -> %% directories. case os:type() of {win32, _} -> - ?line test_directory("/", read_write), - ?line test_directory("c:/", read_write), - ?line test_directory("c:\\", read_write); + test_directory("/", read_write), + test_directory("c:/", read_write), + test_directory("c:\\", read_write); _ -> - ?line test_directory("/", read) + test_directory("/", read) end, - test_server:timetrap_cancel(Dog). + ok. test_directory(Name, ExpectedAccess) -> {ok,FileInfo} = ?FILE_MODULE:read_file_info(Name), {ok,FileInfo} = ?FILE_MODULE:read_file_info(Name, [raw]), #file_info{size=Size,type=Type,access=Access, atime=AccessTime,mtime=ModifyTime} = FileInfo, - ?line io:format("Testing directory ~s", [Name]), - ?line io:format("Directory size is ~p", [Size]), - ?line io:format("Access ~p", [Access]), - ?line io:format("Access time ~p; Modify time~p", - [AccessTime, ModifyTime]), - ?line Type = directory, - ?line Access = ExpectedAccess, - ?line all_integers(tuple_to_list(AccessTime) ++ tuple_to_list(ModifyTime)), - ?line [] = flush(), + io:format("Testing directory ~s", [Name]), + io:format("Directory size is ~p", [Size]), + io:format("Access ~p", [Access]), + io:format("Access time ~p; Modify time~p", + [AccessTime, ModifyTime]), + Type = directory, + Access = ExpectedAccess, + all_integers(tuple_to_list(AccessTime) ++ tuple_to_list(ModifyTime)), + [] = flush(), ok. all_integers([{A,B,C}|T]) -> all_integers([A,B,C|T]); all_integers([Int|Rest]) when is_integer(Int) -> - ?line all_integers(Rest); + all_integers(Rest); all_integers([]) -> ok. %% Try something nonexistent. -file_info_bad(suite) -> []; -file_info_bad(doc) -> []; file_info_bad(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(5)), - ?line RootDir = filename:join([?config(priv_dir, Config)]), + RootDir = filename:join([proplists:get_value(priv_dir, Config)]), FileName = filename:join(RootDir, atom_to_list(?MODULE) ++ "_nonexistent"), {error,enoent} = ?FILE_MODULE:read_file_info(FileName), {error,enoent} = ?FILE_MODULE:read_file_info(FileName, [raw]), - ?line {error, enoent} = ?FILE_MODULE:read_file_info(""), + {error, enoent} = ?FILE_MODULE:read_file_info(""), {error, enoent} = ?FILE_MODULE:read_file_info("", [raw]), - ?line [] = flush(), - ?line test_server:timetrap_cancel(Dog), + [] = flush(), ok. %% Test that the file times behave as they should. -file_info_times(suite) -> []; -file_info_times(doc) -> []; file_info_times(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(60)), %% We have to try this twice, since if the test runs across the change %% of a month the time diff calculations will fail. But it won't happen %% if you run it twice in succession. - ?line test_server:m_out_of_n( - 1,2, - fun() -> ?line file_info_int(Config) end), - ?line test_server:timetrap_cancel(Dog), + test_server:m_out_of_n( + 1,2, + fun() -> file_info_int(Config) end), ok. file_info_int(Config) -> @@ -1338,14 +1462,14 @@ file_info_int(Config) -> %% which is essential for ?FILE_MODULE:file_info/1 to work on %% platforms such as Windows95. - ?line RootDir = filename:join([?config(priv_dir, Config)]), - ?line test_server:format("RootDir = ~p", [RootDir]), + RootDir = filename:join([proplists:get_value(priv_dir, Config)]), + io:format("RootDir = ~p", [RootDir]), - ?line Name = filename:join(RootDir, - atom_to_list(?MODULE) - ++"_file_info.fil"), - ?line {ok,Fd1} = ?FILE_MODULE:open(Name,write), - ?line io:put_chars(Fd1,"foo"), + Name = filename:join(RootDir, + atom_to_list(?MODULE) + ++"_file_info.fil"), + {ok,Fd1} = ?FILE_MODULE:open(Name,write), + io:put_chars(Fd1,"foo"), %% check that the file got a modify date max a few seconds away from now {ok,FileInfo1} = ?FILE_MODULE:read_file_info(Name), @@ -1358,31 +1482,31 @@ file_info_int(Config) -> #file_info{type=regular,atime=AccTime1,mtime=ModTime1} = FileInfo1, - ?line Now = erlang:localtime(), %??? - ?line io:format("Now ~p",[Now]), - ?line io:format("Open file Acc ~p Mod ~p",[AccTime1,ModTime1]), - ?line true = abs(time_dist(filter_atime(Now, Config), - filter_atime(AccTime1, - Config))) < 8, - ?line true = abs(time_dist(Now,ModTime1)) < 8, + Now = erlang:localtime(), %??? + io:format("Now ~p",[Now]), + io:format("Open file Acc ~p Mod ~p",[AccTime1,ModTime1]), + true = abs(time_dist(filter_atime(Now, Config), + filter_atime(AccTime1, + Config))) < 8, + true = abs(time_dist(Now,ModTime1)) < 8, %% Sleep until we can be sure the seconds value has changed. %% Note: FAT-based filesystem (like on Windows 95) have %% a resolution of 2 seconds. - ?line test_server:sleep(test_server:seconds(2.2)), + timer:sleep(2200), %% close the file, and watch the modify date change - ?line ok = ?FILE_MODULE:close(Fd1), + ok = ?FILE_MODULE:close(Fd1), {ok,FileInfo2} = ?FILE_MODULE:read_file_info(Name), {ok,FileInfo2} = ?FILE_MODULE:read_file_info(Name, [raw]), #file_info{size=Size,type=regular,access=Access, atime=AccTime2,mtime=ModTime2} = FileInfo2, - ?line io:format("Closed file Acc ~p Mod ~p",[AccTime2,ModTime2]), - ?line true = time_dist(ModTime1,ModTime2) >= 0, + io:format("Closed file Acc ~p Mod ~p",[AccTime2,ModTime2]), + true = time_dist(ModTime1,ModTime2) >= 0, %% this file is supposed to be binary, so it'd better keep it's size - ?line Size = 3, - ?line Access = read_write, + Size = 3, + Access = read_write, %% Do some directory checking {ok,FileInfo3} = ?FILE_MODULE:read_file_info(RootDir), @@ -1390,12 +1514,12 @@ file_info_int(Config) -> #file_info{size=DSize,type=directory,access=DAccess, atime=AccTime3,mtime=ModTime3} = FileInfo3, %% this dir was modified only a few secs ago - ?line io:format("Dir Acc ~p; Mod ~p; Now ~p", [AccTime3, ModTime3, Now]), - ?line true = abs(time_dist(Now,ModTime3)) < 5, - ?line DAccess = read_write, - ?line io:format("Dir size is ~p",[DSize]), + io:format("Dir Acc ~p; Mod ~p; Now ~p", [AccTime3, ModTime3, Now]), + true = abs(time_dist(Now,ModTime3)) < 5, + DAccess = read_write, + io:format("Dir size is ~p",[DSize]), - ?line [] = flush(), + [] = flush(), ok. %% Filter access times, to copy with a deficiency of FAT file systems @@ -1406,9 +1530,9 @@ filter_atime(Atime, Config) -> true -> case Atime of {Date, _} -> - {Date, {0, 0, 0}}; + {Date, {0, 0, 0}}; {Y, M, D, _, _, _} -> - {Y, M, D, 0, 0, 0} + {Y, M, D, 0, 0, 0} end; false -> Atime @@ -1416,50 +1540,47 @@ filter_atime(Atime, Config) -> %% Test the write_file_info/2 function. -file_write_file_info(suite) -> []; -file_write_file_info(doc) -> []; file_write_file_info(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(10)), - ?line RootDir = get_good_directory(Config), - ?line test_server:format("RootDir = ~p", [RootDir]), + RootDir = get_good_directory(Config), + io:format("RootDir = ~p", [RootDir]), %% Set the file to read only AND update the file times at the same time. %% (This used to fail on Windows NT/95 for a local filesystem.) %% Note: Seconds must be even; see note in file_info_times/1. - ?line Name1 = filename:join(RootDir, - atom_to_list(?MODULE) - ++"_write_file_info_ro"), - ?line ok = ?FILE_MODULE:write_file(Name1, "hello"), - ?line Time = {{1997, 01, 02}, {12, 35, 42}}, - ?line Info = #file_info{mode=8#400, atime=Time, mtime=Time, ctime=Time}, - ?line ok = ?FILE_MODULE:write_file_info(Name1, Info), + Name1 = filename:join(RootDir, + atom_to_list(?MODULE) + ++"_write_file_info_ro"), + ok = ?FILE_MODULE:write_file(Name1, "hello"), + Time = {{1997, 01, 02}, {12, 35, 42}}, + Info = #file_info{mode=8#400, atime=Time, mtime=Time, ctime=Time}, + ok = ?FILE_MODULE:write_file_info(Name1, Info), %% Read back the times. - ?line {ok, ActualInfo} = ?FILE_MODULE:read_file_info(Name1), - ?line #file_info{mode=_Mode, atime=ActAtime, mtime=Time, - ctime=ActCtime} = ActualInfo, - ?line FilteredAtime = filter_atime(Time, Config), - ?line FilteredAtime = filter_atime(ActAtime, Config), - ?line case os:type() of - {win32, _} -> - %% On Windows, "ctime" means creation time and it can - %% be set. - ActCtime = Time; - _ -> - ok - end, - ?line {error, eacces} = ?FILE_MODULE:write_file(Name1, "hello again"), + {ok, ActualInfo} = ?FILE_MODULE:read_file_info(Name1), + #file_info{mode=_Mode, atime=ActAtime, mtime=Time, + ctime=ActCtime} = ActualInfo, + FilteredAtime = filter_atime(Time, Config), + FilteredAtime = filter_atime(ActAtime, Config), + case os:type() of + {win32, _} -> + %% On Windows, "ctime" means creation time and it can + %% be set. + ActCtime = Time; + _ -> + ok + end, + {error, eacces} = ?FILE_MODULE:write_file(Name1, "hello again"), %% Make the file writable again. - ?line ?FILE_MODULE:write_file_info(Name1, #file_info{mode=8#600}), - ?line ok = ?FILE_MODULE:write_file(Name1, "hello again"), + ?FILE_MODULE:write_file_info(Name1, #file_info{mode=8#600}), + ok = ?FILE_MODULE:write_file(Name1, "hello again"), %% And unwritable. - ?line ?FILE_MODULE:write_file_info(Name1, #file_info{mode=8#400}), - ?line {error, eacces} = ?FILE_MODULE:write_file(Name1, "hello again"), + ?FILE_MODULE:write_file_info(Name1, #file_info{mode=8#400}), + {error, eacces} = ?FILE_MODULE:write_file(Name1, "hello again"), %% Same with raw. ?FILE_MODULE:write_file_info(Name1, #file_info{mode=8#600}, [raw]), @@ -1470,634 +1591,635 @@ file_write_file_info(Config) when is_list(Config) -> %% Write the times again. %% Note: Seconds must be even; see note in file_info_times/1. - ?line NewTime = {{1997, 02, 15}, {13, 18, 20}}, - ?line NewInfo = #file_info{atime=NewTime, mtime=NewTime, ctime=NewTime}, - ?line ok = ?FILE_MODULE:write_file_info(Name1, NewInfo), - ?line {ok, ActualInfo2} = ?FILE_MODULE:read_file_info(Name1), - ?line #file_info{atime=NewActAtime, mtime=NewTime, - ctime=NewActCtime} = ActualInfo2, - ?line NewFilteredAtime = filter_atime(NewTime, Config), - ?line NewFilteredAtime = filter_atime(NewActAtime, Config), - ?line case os:type() of - {win32, _} -> NewActCtime = NewTime; - _ -> ok - end, + NewTime = {{1997, 02, 15}, {13, 18, 20}}, + NewInfo = #file_info{atime=NewTime, mtime=NewTime, ctime=NewTime}, + ok = ?FILE_MODULE:write_file_info(Name1, NewInfo), + {ok, ActualInfo2} = ?FILE_MODULE:read_file_info(Name1), + #file_info{atime=NewActAtime, mtime=NewTime, + ctime=NewActCtime} = ActualInfo2, + NewFilteredAtime = filter_atime(NewTime, Config), + NewFilteredAtime = filter_atime(NewActAtime, Config), + case os:type() of + {win32, _} -> NewActCtime = NewTime; + _ -> ok + end, %% The file should still be unwritable. - ?line {error, eacces} = ?FILE_MODULE:write_file(Name1, "hello again"), + {error, eacces} = ?FILE_MODULE:write_file(Name1, "hello again"), %% Make the file writeable again, so that we can remove the %% test suites ... :-) - ?line ?FILE_MODULE:write_file_info(Name1, #file_info{mode=8#600}), + ?FILE_MODULE:write_file_info(Name1, #file_info{mode=8#600}), - ?line [] = flush(), - ?line test_server:timetrap_cancel(Dog), + [] = flush(), + ok. + +file_wfi_helpers(Config) when is_list(Config) -> + RootDir = get_good_directory(Config), + io:format("RootDir = ~p", [RootDir]), + + Name = filename:join(RootDir, + atom_to_list(?MODULE) ++ "_wfi_helpers"), + + ok = ?FILE_MODULE:write_file(Name, "hello again"), + NewTime = {{1997, 02, 15}, {13, 18, 20}}, + ok = ?FILE_MODULE:change_time(Name, NewTime, NewTime), + + {ok, #file_info{atime=NewActAtime, mtime=NewTime}} = + ?FILE_MODULE:read_file_info(Name), + + NewFilteredAtime = filter_atime(NewTime, Config), + NewFilteredAtime = filter_atime(NewActAtime, Config), + + %% Make the file unwritable + ok = ?FILE_MODULE:change_mode(Name, 8#400), + {error, eacces} = ?FILE_MODULE:write_file(Name, "hello again"), + + %% ... and writable again + ok = ?FILE_MODULE:change_mode(Name, 8#600), + ok = ?FILE_MODULE:write_file(Name, "hello again"), + + %% We have no idea which users will work, so all we can do is to check + %% that it returns enoent instead of crashing. + {error, enoent} = ?FILE_MODULE:change_group("bogus file name", 0), + {error, enoent} = ?FILE_MODULE:change_owner("bogus file name", 0), + + [] = flush(), ok. %% Returns a directory on a file system that has correct file times. get_good_directory(Config) -> - ?line ?config(priv_dir, Config). + proplists:get_value(priv_dir, Config). -consult1(suite) -> []; -consult1(doc) -> []; consult1(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(5)), - ?line RootDir = ?config(priv_dir,Config), - ?line Name = filename:join(RootDir, - atom_to_list(?MODULE) - ++"_consult.fil"), - ?line {ok,Fd1} = ?FILE_MODULE:open(Name,write), + RootDir = proplists:get_value(priv_dir,Config), + Name = filename:join(RootDir, + atom_to_list(?MODULE) + ++"_consult.fil"), + {ok,Fd1} = ?FILE_MODULE:open(Name,write), %% note that there is no final \n (only a space) - ?line io:format(Fd1, - "{this,[is,1.0],'journey'}.\n\"into\". (sound). ", - []), - ?line ok = ?FILE_MODULE:close(Fd1), - ?line {ok,[{this,[is,1.0],journey},"into",sound]} = + io:format(Fd1, + "{this,[is,1.0],'journey'}.\n\"into\". (sound). ", + []), + ok = ?FILE_MODULE:close(Fd1), + {ok,[{this,[is,1.0],journey},"into",sound]} = ?FILE_MODULE:consult(Name), - ?line {ok,Fd2} = ?FILE_MODULE:open(Name,write), + {ok,Fd2} = ?FILE_MODULE:open(Name,write), %% note the missing double quote - ?line io:format( - Fd2,"{this,[is,1.0],'journey'}.\n \"into. (sound). ",[]), - ?line ok = ?FILE_MODULE:close(Fd2), - ?line {error, {_, _, _} = Msg} = ?FILE_MODULE:consult(Name), - ?line io:format("Errmsg: ~p",[Msg]), + io:format( + Fd2,"{this,[is,1.0],'journey'}.\n \"into. (sound). ",[]), + ok = ?FILE_MODULE:close(Fd2), + {error, {_, _, _} = Msg} = ?FILE_MODULE:consult(Name), + io:format("Errmsg: ~p",[Msg]), - ?line {error, enoent} = ?FILE_MODULE:consult(Name ++ ".nonexistent"), + {error, enoent} = ?FILE_MODULE:consult(Name ++ ".nonexistent"), - ?line [] = flush(), - ?line test_server:timetrap_cancel(Dog), + [] = flush(), ok. -path_consult(suite) -> []; -path_consult(doc) -> []; path_consult(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(5)), - ?line RootDir = ?config(priv_dir,Config), - ?line FileName = atom_to_list(?MODULE)++"_path_consult.fil", - ?line Name = filename:join(RootDir, FileName), - ?line {ok,Fd1} = ?FILE_MODULE:open(Name,write), - ?line io:format(Fd1,"{this,is,a,journey,into,sound}.\n",[]), - ?line ok = ?FILE_MODULE:close(Fd1), + RootDir = proplists:get_value(priv_dir,Config), + FileName = atom_to_list(?MODULE)++"_path_consult.fil", + Name = filename:join(RootDir, FileName), + {ok,Fd1} = ?FILE_MODULE:open(Name,write), + io:format(Fd1,"{this,is,a,journey,into,sound}.\n",[]), + ok = ?FILE_MODULE:close(Fd1), %% File last in path - ?line {ok,[{this,is,a,journey,into,sound}],Dir} = + {ok,[{this,is,a,journey,into,sound}],Dir} = ?FILE_MODULE:path_consult( - [filename:join(RootDir, "dir1"), - filename:join(RootDir, ".."), - filename:join(RootDir, "dir2"), - RootDir], FileName), - ?line true = lists:prefix(RootDir,Dir), + [filename:join(RootDir, "dir1"), + filename:join(RootDir, ".."), + filename:join(RootDir, "dir2"), + RootDir], FileName), + true = lists:prefix(RootDir,Dir), %% While maybe not an error, it may be worth noting that %% when the full path to a file is given, it's always found %% regardless of the contents of the path - ?line {ok,_,_} = ?FILE_MODULE:path_consult(["nosuch1","nosuch2"],Name), + {ok,_,_} = ?FILE_MODULE:path_consult(["nosuch1","nosuch2"],Name), - ?line [] = flush(), - ?line test_server:timetrap_cancel(Dog), + [] = flush(), ok. -eval1(suite) -> []; -eval1(doc) -> []; eval1(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(5)), - ?line RootDir = ?config(priv_dir,Config), - ?line Name = filename:join(RootDir, - atom_to_list(?MODULE)++"_eval.fil"), - ?line {ok,Fd1} = ?FILE_MODULE:open(Name,write), + RootDir = proplists:get_value(priv_dir,Config), + Name = filename:join(RootDir, + atom_to_list(?MODULE)++"_eval.fil"), + {ok,Fd1} = ?FILE_MODULE:open(Name,write), %% note that there is no final \n (only a space) - ?line io:format(Fd1,"put(evaluated_ok,\ntrue). ",[]), - ?line ok = ?FILE_MODULE:close(Fd1), - ?line ok = ?FILE_MODULE:eval(Name), - ?line true = get(evaluated_ok), - - ?line {ok,Fd2} = ?FILE_MODULE:open(Name,write), + io:format(Fd1,"put(evaluated_ok,\ntrue). ",[]), + ok = ?FILE_MODULE:close(Fd1), + ok = ?FILE_MODULE:eval(Name), + true = get(evaluated_ok), + + {ok,Fd2} = ?FILE_MODULE:open(Name,write), %% note that there is no final \n (only a space) - ?line io:format(Fd2,"put(evaluated_ok,\nR). ",[]), - ?line ok = ?FILE_MODULE:close(Fd2), - ?line ok = ?FILE_MODULE:eval( - Name, - erl_eval:add_binding('R', true, erl_eval:new_bindings())), - ?line true = get(evaluated_ok), - - ?line {ok,Fd3} = ?FILE_MODULE:open(Name,write), + io:format(Fd2,"put(evaluated_ok,\nR). ",[]), + ok = ?FILE_MODULE:close(Fd2), + ok = ?FILE_MODULE:eval( + Name, + erl_eval:add_binding('R', true, erl_eval:new_bindings())), + true = get(evaluated_ok), + + {ok,Fd3} = ?FILE_MODULE:open(Name,write), %% garbled - ?line io:format(Fd3,"puGARBLED-GARBLED\ntrue). ",[]), - ?line ok = ?FILE_MODULE:close(Fd3), - ?line {error, {_, _, _} = Msg} = ?FILE_MODULE:eval(Name), - ?line io:format("Errmsg1: ~p",[Msg]), - - ?line {error, enoent} = ?FILE_MODULE:eval(Name ++ ".nonexistent"), - - ?line [] = flush(), - ?line test_server:timetrap_cancel(Dog), + io:format(Fd3,"puGARBLED-GARBLED\ntrue). ",[]), + ok = ?FILE_MODULE:close(Fd3), + {error, {_, _, _} = Msg} = ?FILE_MODULE:eval(Name), + io:format("Errmsg1: ~p",[Msg]), + + {error, enoent} = ?FILE_MODULE:eval(Name ++ ".nonexistent"), + + [] = flush(), ok. -path_eval(suite) -> []; -path_eval(doc) -> []; path_eval(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(5)), - ?line RootDir = ?config(priv_dir,Config), - ?line FileName = atom_to_list(?MODULE)++"_path_eval.fil", - ?line Name = filename:join(RootDir, FileName), - ?line {ok,Fd1} = ?FILE_MODULE:open(Name,write), - ?line io:format(Fd1,"put(evaluated_ok,true).\n",[]), - ?line ok = ?FILE_MODULE:close(Fd1), + RootDir = proplists:get_value(priv_dir,Config), + FileName = atom_to_list(?MODULE)++"_path_eval.fil", + Name = filename:join(RootDir, FileName), + {ok,Fd1} = ?FILE_MODULE:open(Name,write), + io:format(Fd1,"put(evaluated_ok,true).\n",[]), + ok = ?FILE_MODULE:close(Fd1), %% File last in path - ?line {ok,Dir} = + {ok,Dir} = ?FILE_MODULE:path_eval( - [filename:join(RootDir, "dir1"), - filename:join(RootDir, ".."), - filename:join(RootDir, "dir2"), - RootDir],FileName), - ?line true = get(evaluated_ok), - ?line true = lists:prefix(RootDir,Dir), - + [filename:join(RootDir, "dir1"), + filename:join(RootDir, ".."), + filename:join(RootDir, "dir2"), + RootDir],FileName), + true = get(evaluated_ok), + true = lists:prefix(RootDir,Dir), + %% While maybe not an error, it may be worth noting that %% when the full path to a file is given, it's always found %% regardless of the contents of the path - ?line {ok,Fd2} = ?FILE_MODULE:open(Name,write), - ?line io:format(Fd2,"put(evaluated_ok,R).\n",[]), - ?line ok = ?FILE_MODULE:close(Fd2), - ?line {ok,_} = ?FILE_MODULE:path_eval( - ["nosuch1","nosuch2"], - Name, - erl_eval:add_binding('R', true, erl_eval:new_bindings())), - - ?line [] = flush(), - ?line test_server:timetrap_cancel(Dog), + {ok,Fd2} = ?FILE_MODULE:open(Name,write), + io:format(Fd2,"put(evaluated_ok,R).\n",[]), + ok = ?FILE_MODULE:close(Fd2), + {ok,_} = ?FILE_MODULE:path_eval( + ["nosuch1","nosuch2"], + Name, + erl_eval:add_binding('R', true, erl_eval:new_bindings())), + + [] = flush(), ok. -script1(suite) -> []; -script1(doc) -> ""; script1(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(5)), - ?line RootDir = ?config(priv_dir,Config), - ?line Name = filename:join(RootDir, - atom_to_list(?MODULE)++"_script.fil"), - ?line {ok,Fd1} = ?FILE_MODULE:open(Name,write), + RootDir = proplists:get_value(priv_dir,Config), + Name = filename:join(RootDir, + atom_to_list(?MODULE)++"_script.fil"), + {ok,Fd1} = ?FILE_MODULE:open(Name,write), %% note that there is no final \n (only a space) - ?line io:format(Fd1,"A = 11,\nB = 6,\nA+B. ",[]), - ?line ok = ?FILE_MODULE:close(Fd1), - ?line {ok,17} = ?FILE_MODULE:script(Name), - - ?line {ok,Fd2} = ?FILE_MODULE:open(Name,write), + io:format(Fd1,"A = 11,\nB = 6,\nA+B. ",[]), + ok = ?FILE_MODULE:close(Fd1), + {ok,17} = ?FILE_MODULE:script(Name), + + {ok,Fd2} = ?FILE_MODULE:open(Name,write), %% note that there is no final \n (only a space) - ?line io:format(Fd2,"A = 11,\nA+B. ",[]), - ?line ok = ?FILE_MODULE:close(Fd2), - ?line {ok,17} = ?FILE_MODULE:script( - Name, - erl_eval:add_binding('B', 6, erl_eval:new_bindings())), - - ?line {ok,Fd3} = ?FILE_MODULE:open(Name,write), - ?line io:format(Fd3,"A = 11,\nB = six,\nA+B. ",[]), - ?line ok = ?FILE_MODULE:close(Fd3), - ?line {error, {_, _, _} = Msg} = ?FILE_MODULE:script(Name), - ?line io:format("Errmsg1: ~p",[Msg]), - - ?line {error, enoent} = ?FILE_MODULE:script(Name ++ ".nonexistent"), - - ?line [] = flush(), - ?line test_server:timetrap_cancel(Dog), + io:format(Fd2,"A = 11,\nA+B. ",[]), + ok = ?FILE_MODULE:close(Fd2), + {ok,17} = ?FILE_MODULE:script( + Name, + erl_eval:add_binding('B', 6, erl_eval:new_bindings())), + + {ok,Fd3} = ?FILE_MODULE:open(Name,write), + io:format(Fd3,"A = 11,\nB = six,\nA+B. ",[]), + ok = ?FILE_MODULE:close(Fd3), + {error, {_, _, _} = Msg} = ?FILE_MODULE:script(Name), + io:format("Errmsg1: ~p",[Msg]), + + {error, enoent} = ?FILE_MODULE:script(Name ++ ".nonexistent"), + + [] = flush(), ok. - -path_script(suite) -> []; -path_script(doc) -> []; + path_script(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(5)), - ?line RootDir = ?config(priv_dir,Config), - ?line FileName = atom_to_list(?MODULE)++"_path_script.fil", - ?line Name = filename:join(RootDir, FileName), - ?line {ok,Fd1} = ?FILE_MODULE:open(Name,write), - ?line io:format(Fd1,"A = 11,\nB = 6,\nA+B.\n",[]), - ?line ok = ?FILE_MODULE:close(Fd1), + RootDir = proplists:get_value(priv_dir,Config), + FileName = atom_to_list(?MODULE)++"_path_script.fil", + Name = filename:join(RootDir, FileName), + {ok,Fd1} = ?FILE_MODULE:open(Name,write), + io:format(Fd1,"A = 11,\nB = 6,\nA+B.\n",[]), + ok = ?FILE_MODULE:close(Fd1), %% File last in path - ?line {ok, 17, Dir} = + {ok, 17, Dir} = ?FILE_MODULE:path_script( [filename:join(RootDir, "dir1"), filename:join(RootDir, ".."), filename:join(RootDir, "dir2"), RootDir],FileName), - ?line true = lists:prefix(RootDir,Dir), - + true = lists:prefix(RootDir,Dir), + %% While maybe not an error, it may be worth noting that %% when the full path to a file is given, it's always found %% regardless of the contents of the path - ?line {ok,Fd2} = ?FILE_MODULE:open(Name,write), - ?line io:format(Fd2,"A = 11,\nA+B.",[]), - ?line ok = ?FILE_MODULE:close(Fd2), - ?line {ok, 17, Dir} = + {ok,Fd2} = ?FILE_MODULE:open(Name,write), + io:format(Fd2,"A = 11,\nA+B.",[]), + ok = ?FILE_MODULE:close(Fd2), + {ok, 17, Dir} = ?FILE_MODULE:path_script( ["nosuch1","nosuch2"], Name, erl_eval:add_binding('B', 6, erl_eval:new_bindings())), - - ?line [] = flush(), - ?line test_server:timetrap_cancel(Dog), + + [] = flush(), ok. - -truncate(suite) -> []; -truncate(doc) -> []; + truncate(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(5)), - ?line RootDir = ?config(priv_dir,Config), - ?line Name = filename:join(RootDir, - atom_to_list(?MODULE) - ++"_truncate.fil"), + RootDir = proplists:get_value(priv_dir,Config), + Name = filename:join(RootDir, + atom_to_list(?MODULE) + ++"_truncate.fil"), %% Create a file with some data. - ?line MyData = "0123456789abcdefghijklmnopqrstuvxyz", - ?line ok = ?FILE_MODULE:write_file(Name, MyData), + MyData = "0123456789abcdefghijklmnopqrstuvxyz", + ok = ?FILE_MODULE:write_file(Name, MyData), %% Truncate the file to 10 characters. - ?line {ok, Fd} = ?FILE_MODULE:open(Name, read_write), - ?line {ok, 10} = ?FILE_MODULE:position(Fd, 10), - ?line ok = ?FILE_MODULE:truncate(Fd), - ?line ok = ?FILE_MODULE:close(Fd), + {ok, Fd} = ?FILE_MODULE:open(Name, read_write), + {ok, 10} = ?FILE_MODULE:position(Fd, 10), + ok = ?FILE_MODULE:truncate(Fd), + ok = ?FILE_MODULE:close(Fd), %% Read back the file and check that it has been truncated. - ?line Expected = list_to_binary("0123456789"), - ?line {ok, Expected} = ?FILE_MODULE:read_file(Name), + Expected = list_to_binary("0123456789"), + {ok, Expected} = ?FILE_MODULE:read_file(Name), %% Open the file read only and verify that it is not possible to %% truncate it, OTP-1960 - ?line {ok, Fd2} = ?FILE_MODULE:open(Name, read), - ?line {ok, 5} = ?FILE_MODULE:position(Fd2, 5), - ?line {error, _} = ?FILE_MODULE:truncate(Fd2), + {ok, Fd2} = ?FILE_MODULE:open(Name, read), + {ok, 5} = ?FILE_MODULE:position(Fd2, 5), + {error, _} = ?FILE_MODULE:truncate(Fd2), - ?line [] = flush(), - ?line test_server:timetrap_cancel(Dog), + [] = flush(), ok. -datasync(suite) -> []; -datasync(doc) -> "Tests that ?FILE_MODULE:datasync/1 at least doesn't crash."; +%% Tests that ?FILE_MODULE:datasync/1 at least doesn't crash. datasync(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(5)), - ?line PrivDir = ?config(priv_dir, Config), - ?line Sync = filename:join(PrivDir, - atom_to_list(?MODULE) - ++"_sync.fil"), + PrivDir = proplists:get_value(priv_dir, Config), + Sync = filename:join(PrivDir, + atom_to_list(?MODULE) + ++"_sync.fil"), %% Raw open. - ?line {ok, Fd} = ?FILE_MODULE:open(Sync, [write, raw]), - ?line ok = ?FILE_MODULE:datasync(Fd), - ?line ok = ?FILE_MODULE:close(Fd), + {ok, Fd} = ?FILE_MODULE:open(Sync, [write, raw]), + ok = ?FILE_MODULE:datasync(Fd), + ok = ?FILE_MODULE:close(Fd), %% Ordinary open. - ?line {ok, Fd2} = ?FILE_MODULE:open(Sync, [write]), - ?line ok = ?FILE_MODULE:datasync(Fd2), - ?line ok = ?FILE_MODULE:close(Fd2), + {ok, Fd2} = ?FILE_MODULE:open(Sync, [write]), + ok = ?FILE_MODULE:datasync(Fd2), + ok = ?FILE_MODULE:close(Fd2), - ?line [] = flush(), - ?line test_server:timetrap_cancel(Dog), + [] = flush(), ok. -sync(suite) -> []; -sync(doc) -> "Tests that ?FILE_MODULE:sync/1 at least doesn't crash."; +%% Tests that ?FILE_MODULE:sync/1 at least doesn't crash. sync(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(5)), - ?line PrivDir = ?config(priv_dir, Config), - ?line Sync = filename:join(PrivDir, - atom_to_list(?MODULE) - ++"_sync.fil"), + PrivDir = proplists:get_value(priv_dir, Config), + Sync = filename:join(PrivDir, + atom_to_list(?MODULE) + ++"_sync.fil"), %% Raw open. - ?line {ok, Fd} = ?FILE_MODULE:open(Sync, [write, raw]), - ?line ok = ?FILE_MODULE:sync(Fd), - ?line ok = ?FILE_MODULE:close(Fd), + {ok, Fd} = ?FILE_MODULE:open(Sync, [write, raw]), + ok = ?FILE_MODULE:sync(Fd), + ok = ?FILE_MODULE:close(Fd), %% Ordinary open. - ?line {ok, Fd2} = ?FILE_MODULE:open(Sync, [write]), - ?line ok = ?FILE_MODULE:sync(Fd2), - ?line ok = ?FILE_MODULE:close(Fd2), + {ok, Fd2} = ?FILE_MODULE:open(Sync, [write]), + ok = ?FILE_MODULE:sync(Fd2), + ok = ?FILE_MODULE:close(Fd2), - ?line [] = flush(), - ?line test_server:timetrap_cancel(Dog), + [] = flush(), ok. -advise(suite) -> []; -advise(doc) -> "Tests that ?FILE_MODULE:advise/4 at least doesn't crash."; +%% Tests that ?FILE_MODULE:advise/4 at least doesn't crash. advise(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(5)), - ?line PrivDir = ?config(priv_dir, Config), - ?line Advise = filename:join(PrivDir, - atom_to_list(?MODULE) - ++"_advise.fil"), + PrivDir = proplists:get_value(priv_dir, Config), + Advise = filename:join(PrivDir, + atom_to_list(?MODULE) + ++"_advise.fil"), Line1 = "Hello\n", Line2 = "World!\n", - ?line {ok, Fd} = ?FILE_MODULE:open(Advise, [write]), - ?line ok = ?FILE_MODULE:advise(Fd, 0, 0, normal), - ?line ok = io:format(Fd, "~s", [Line1]), - ?line ok = io:format(Fd, "~s", [Line2]), - ?line ok = ?FILE_MODULE:close(Fd), - - ?line {ok, Fd2} = ?FILE_MODULE:open(Advise, [write]), - ?line ok = ?FILE_MODULE:advise(Fd2, 0, 0, random), - ?line ok = io:format(Fd2, "~s", [Line1]), - ?line ok = io:format(Fd2, "~s", [Line2]), - ?line ok = ?FILE_MODULE:close(Fd2), - - ?line {ok, Fd3} = ?FILE_MODULE:open(Advise, [write]), - ?line ok = ?FILE_MODULE:advise(Fd3, 0, 0, sequential), - ?line ok = io:format(Fd3, "~s", [Line1]), - ?line ok = io:format(Fd3, "~s", [Line2]), - ?line ok = ?FILE_MODULE:close(Fd3), - - ?line {ok, Fd4} = ?FILE_MODULE:open(Advise, [write]), - ?line ok = ?FILE_MODULE:advise(Fd4, 0, 0, will_need), - ?line ok = io:format(Fd4, "~s", [Line1]), - ?line ok = io:format(Fd4, "~s", [Line2]), - ?line ok = ?FILE_MODULE:close(Fd4), - - ?line {ok, Fd5} = ?FILE_MODULE:open(Advise, [write]), - ?line ok = ?FILE_MODULE:advise(Fd5, 0, 0, dont_need), - ?line ok = io:format(Fd5, "~s", [Line1]), - ?line ok = io:format(Fd5, "~s", [Line2]), - ?line ok = ?FILE_MODULE:close(Fd5), - - ?line {ok, Fd6} = ?FILE_MODULE:open(Advise, [write]), - ?line ok = ?FILE_MODULE:advise(Fd6, 0, 0, no_reuse), - ?line ok = io:format(Fd6, "~s", [Line1]), - ?line ok = io:format(Fd6, "~s", [Line2]), - ?line ok = ?FILE_MODULE:close(Fd6), - - ?line {ok, Fd7} = ?FILE_MODULE:open(Advise, [write]), - ?line {error, einval} = ?FILE_MODULE:advise(Fd7, 0, 0, bad_advise), - ?line ok = ?FILE_MODULE:close(Fd7), + {ok, Fd} = ?FILE_MODULE:open(Advise, [write]), + ok = ?FILE_MODULE:advise(Fd, 0, 0, normal), + ok = io:format(Fd, "~s", [Line1]), + ok = io:format(Fd, "~s", [Line2]), + ok = ?FILE_MODULE:close(Fd), + + {ok, Fd2} = ?FILE_MODULE:open(Advise, [write]), + ok = ?FILE_MODULE:advise(Fd2, 0, 0, random), + ok = io:format(Fd2, "~s", [Line1]), + ok = io:format(Fd2, "~s", [Line2]), + ok = ?FILE_MODULE:close(Fd2), + + {ok, Fd3} = ?FILE_MODULE:open(Advise, [write]), + ok = ?FILE_MODULE:advise(Fd3, 0, 0, sequential), + ok = io:format(Fd3, "~s", [Line1]), + ok = io:format(Fd3, "~s", [Line2]), + ok = ?FILE_MODULE:close(Fd3), + + {ok, Fd4} = ?FILE_MODULE:open(Advise, [write]), + ok = ?FILE_MODULE:advise(Fd4, 0, 0, will_need), + ok = io:format(Fd4, "~s", [Line1]), + ok = io:format(Fd4, "~s", [Line2]), + ok = ?FILE_MODULE:close(Fd4), + + {ok, Fd5} = ?FILE_MODULE:open(Advise, [write]), + ok = ?FILE_MODULE:advise(Fd5, 0, 0, dont_need), + ok = io:format(Fd5, "~s", [Line1]), + ok = io:format(Fd5, "~s", [Line2]), + ok = ?FILE_MODULE:close(Fd5), + + {ok, Fd6} = ?FILE_MODULE:open(Advise, [write]), + ok = ?FILE_MODULE:advise(Fd6, 0, 0, no_reuse), + ok = io:format(Fd6, "~s", [Line1]), + ok = io:format(Fd6, "~s", [Line2]), + ok = ?FILE_MODULE:close(Fd6), + + {ok, Fd7} = ?FILE_MODULE:open(Advise, [write]), + {error, einval} = ?FILE_MODULE:advise(Fd7, 0, 0, bad_advise), + ok = ?FILE_MODULE:close(Fd7), %% test write without advise, then a read after an advise - ?line {ok, Fd8} = ?FILE_MODULE:open(Advise, [write]), - ?line ok = io:format(Fd8, "~s", [Line1]), - ?line ok = io:format(Fd8, "~s", [Line2]), - ?line ok = ?FILE_MODULE:close(Fd8), - ?line {ok, Fd9} = ?FILE_MODULE:open(Advise, [read]), + {ok, Fd8} = ?FILE_MODULE:open(Advise, [write]), + ok = io:format(Fd8, "~s", [Line1]), + ok = io:format(Fd8, "~s", [Line2]), + ok = ?FILE_MODULE:close(Fd8), + {ok, Fd9} = ?FILE_MODULE:open(Advise, [read]), Offset = 0, %% same as a 0 length in some implementations Length = length(Line1) + length(Line2), - ?line ok = ?FILE_MODULE:advise(Fd9, Offset, Length, sequential), - ?line {ok, Line1} = ?FILE_MODULE:read_line(Fd9), - ?line {ok, Line2} = ?FILE_MODULE:read_line(Fd9), - ?line eof = ?FILE_MODULE:read_line(Fd9), - ?line ok = ?FILE_MODULE:close(Fd9), - - ?line [] = flush(), - ?line test_server:timetrap_cancel(Dog), + ok = ?FILE_MODULE:advise(Fd9, Offset, Length, sequential), + {ok, Line1} = ?FILE_MODULE:read_line(Fd9), + {ok, Line2} = ?FILE_MODULE:read_line(Fd9), + eof = ?FILE_MODULE:read_line(Fd9), + ok = ?FILE_MODULE:close(Fd9), + + [] = flush(), ok. -allocate(suite) -> []; -allocate(doc) -> "Tests that ?FILE_MODULE:allocate/3 at least doesn't crash."; +%% Tests that ?FILE_MODULE:allocate/3 at least doesn't crash. allocate(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(5)), - ?line PrivDir = ?config(priv_dir, Config), - ?line Allocate = filename:join(PrivDir, - atom_to_list(?MODULE) - ++"_allocate.fil"), + PrivDir = proplists:get_value(priv_dir, Config), + Allocate = filename:join(PrivDir, + atom_to_list(?MODULE) + ++"_allocate.fil"), Line1 = "Hello\n", Line2 = "World!\n", - ?line {ok, Fd} = ?FILE_MODULE:open(Allocate, [write, binary]), + {ok, Fd} = ?FILE_MODULE:open(Allocate, [write, binary]), allocate_and_assert(Fd, 1, iolist_size([Line1, Line2])), - ?line ok = io:format(Fd, "~s", [Line1]), - ?line ok = io:format(Fd, "~s", [Line2]), - ?line ok = ?FILE_MODULE:close(Fd), + ok = io:format(Fd, "~s", [Line1]), + ok = io:format(Fd, "~s", [Line2]), + ok = ?FILE_MODULE:close(Fd), - ?line {ok, Fd2} = ?FILE_MODULE:open(Allocate, [write, binary]), + {ok, Fd2} = ?FILE_MODULE:open(Allocate, [write, binary]), allocate_and_assert(Fd2, 1, iolist_size(Line1)), - ?line ok = io:format(Fd2, "~s", [Line1]), - ?line ok = io:format(Fd2, "~s", [Line2]), - ?line ok = ?FILE_MODULE:close(Fd2), + ok = io:format(Fd2, "~s", [Line1]), + ok = io:format(Fd2, "~s", [Line2]), + ok = ?FILE_MODULE:close(Fd2), - ?line {ok, Fd3} = ?FILE_MODULE:open(Allocate, [write, binary]), + {ok, Fd3} = ?FILE_MODULE:open(Allocate, [write, binary]), allocate_and_assert(Fd3, 1, iolist_size(Line1) + 1), - ?line ok = io:format(Fd3, "~s", [Line1]), - ?line ok = io:format(Fd3, "~s", [Line2]), - ?line ok = ?FILE_MODULE:close(Fd3), + ok = io:format(Fd3, "~s", [Line1]), + ok = io:format(Fd3, "~s", [Line2]), + ok = ?FILE_MODULE:close(Fd3), - ?line {ok, Fd4} = ?FILE_MODULE:open(Allocate, [write, binary]), + {ok, Fd4} = ?FILE_MODULE:open(Allocate, [write, binary]), allocate_and_assert(Fd4, 1, 4 * iolist_size([Line1, Line2])), - ?line ok = io:format(Fd4, "~s", [Line1]), - ?line ok = io:format(Fd4, "~s", [Line2]), - ?line ok = ?FILE_MODULE:close(Fd4), + ok = io:format(Fd4, "~s", [Line1]), + ok = io:format(Fd4, "~s", [Line2]), + ok = ?FILE_MODULE:close(Fd4), - ?line [] = flush(), - ?line test_server:timetrap_cancel(Dog), + [] = flush(), ok. allocate_and_assert(Fd, Offset, Length) -> - % Just verify that calls to ?PRIM_FILE:allocate/3 don't crash or have - % any other negative side effect. We can't really asssert against a - % specific return value, because support for file space pre-allocation - % depends on the OS, OS version and underlying filesystem. - % - % The Linux kernel added support for fallocate() in version 2.6.23, - % which currently works only for the ext4, ocfs2, xfs and btrfs file - % systems. posix_fallocate() is available in glibc as of version - % 2.1.94, but it was buggy until glibc version 2.7. - % - % Mac OS X, as of version 10.3, supports the fcntl operation F_PREALLOCATE. - % - % Solaris supports posix_fallocate() but only for the UFS file system - % apparently (not supported for ZFS). - % - % FreeBSD 9.0 is the first FreeBSD release supporting posix_fallocate(). - % - % For Windows there's apparently no way to pre-allocate file space, at - % least with same semantics as posix_fallocate(), fallocate() and - % fcntl F_PREALLOCATE. + %% Just verify that calls to ?PRIM_FILE:allocate/3 don't crash or have + %% any other negative side effect. We can't really asssert against a + %% specific return value, because support for file space pre-allocation + %% depends on the OS, OS version and underlying filesystem. + %% + %% The Linux kernel added support for fallocate() in version 2.6.23, + %% which currently works only for the ext4, ocfs2, xfs and btrfs file + %% systems. posix_fallocate() is available in glibc as of version + %% 2.1.94, but it was buggy until glibc version 2.7. + %% + %% Mac OS X, as of version 10.3, supports the fcntl operation F_PREALLOCATE. + %% + %% Solaris supports posix_fallocate() but only for the UFS file system + %% apparently (not supported for ZFS). + %% + %% FreeBSD 9.0 is the first FreeBSD release supporting posix_fallocate(). + %% + %% For Windows there's apparently no way to pre-allocate file space, at + %% least with same semantics as posix_fallocate(), fallocate() and + %% fcntl F_PREALLOCATE. Result = ?FILE_MODULE:allocate(Fd, Offset, Length), case os:type() of {win32, _} -> - ?line {error, enotsup} = Result; + {error, enotsup} = Result; _ -> - ?line _ = Result + _ = Result end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -delete(suite) -> []; -delete(doc) -> []; delete(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(5)), - ?line RootDir = ?config(priv_dir,Config), - ?line Name = filename:join(RootDir, - atom_to_list(?MODULE) - ++"_delete.fil"), - ?line {ok, Fd1} = ?FILE_MODULE:open(Name, write), - ?line io:format(Fd1,"ok.\n",[]), - ?line ok = ?FILE_MODULE:close(Fd1), + RootDir = proplists:get_value(priv_dir,Config), + Name = filename:join(RootDir, + atom_to_list(?MODULE) + ++"_delete.fil"), + {ok, Fd1} = ?FILE_MODULE:open(Name, write), + io:format(Fd1,"ok.\n",[]), + ok = ?FILE_MODULE:close(Fd1), %% Check that the file is readable - ?line {ok, Fd2} = ?FILE_MODULE:open(Name, read), - ?line ok = ?FILE_MODULE:close(Fd2), - ?line ok = ?FILE_MODULE:delete(Name), + {ok, Fd2} = ?FILE_MODULE:open(Name, read), + ok = ?FILE_MODULE:close(Fd2), + ok = ?FILE_MODULE:delete(Name), %% Check that the file is not readable anymore - ?line {error, _} = ?FILE_MODULE:open(Name, read), + {error, _} = ?FILE_MODULE:open(Name, read), %% Try deleting a nonexistent file - ?line {error, enoent} = ?FILE_MODULE:delete(Name), - ?line [] = flush(), - ?line test_server:timetrap_cancel(Dog), + {error, enoent} = ?FILE_MODULE:delete(Name), + [] = flush(), ok. -rename(suite) ->[]; -rename(doc) ->[]; rename(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(5)), - ?line RootDir = ?config(priv_dir,Config), - ?line FileName1 = atom_to_list(?MODULE)++"_rename.fil", - ?line FileName2 = atom_to_list(?MODULE)++"_rename.ful", - ?line Name1 = filename:join(RootDir, FileName1), - ?line Name2 = filename:join(RootDir, FileName2), - ?line {ok,Fd1} = ?FILE_MODULE:open(Name1,write), - ?line ok = ?FILE_MODULE:close(Fd1), + RootDir = proplists:get_value(priv_dir,Config), + FileName1 = atom_to_list(?MODULE)++"_rename.fil", + FileName2 = atom_to_list(?MODULE)++"_rename.ful", + Name1 = filename:join(RootDir, FileName1), + Name2 = filename:join(RootDir, FileName2), + {ok,Fd1} = ?FILE_MODULE:open(Name1,write), + ok = ?FILE_MODULE:close(Fd1), %% Rename, and check that id really changed name - ?line ok = ?FILE_MODULE:rename(Name1,Name2), - ?line {error, _} = ?FILE_MODULE:open(Name1,read), - ?line {ok,Fd2} = ?FILE_MODULE:open(Name2,read), - ?line ok = ?FILE_MODULE:close(Fd2), + ok = ?FILE_MODULE:rename(Name1,Name2), + {error, _} = ?FILE_MODULE:open(Name1,read), + {ok,Fd2} = ?FILE_MODULE:open(Name2,read), + ok = ?FILE_MODULE:close(Fd2), %% Try renaming something to itself - ?line ok = ?FILE_MODULE:rename(Name2,Name2), + ok = ?FILE_MODULE:rename(Name2,Name2), %% Try renaming something that doesn't exist - ?line {error, enoent} = ?FILE_MODULE:rename(Name1,Name2), + {error, enoent} = ?FILE_MODULE:rename(Name1,Name2), %% Try renaming to something else than a string - ?line {error, badarg} = ?FILE_MODULE:rename(Name1,{foo,bar}), - + {error, badarg} = ?FILE_MODULE:rename(Name1,{foo,bar}), + %% Move between directories - ?line DirName1 = filename:join(RootDir, - atom_to_list(?MODULE) - ++"_rename_dir"), - ?line DirName2 = filename:join(RootDir, - atom_to_list(?MODULE) - ++"_second_rename_dir"), - ?line Name1foo = filename:join(DirName1, "foo.fil"), - ?line Name2foo = filename:join(DirName2, "foo.fil"), - ?line Name2bar = filename:join(DirName2, "bar.dir"), - ?line ok = ?FILE_MODULE:make_dir(DirName1), + DirName1 = filename:join(RootDir, + atom_to_list(?MODULE) + ++"_rename_dir"), + DirName2 = filename:join(RootDir, + atom_to_list(?MODULE) + ++"_second_rename_dir"), + Name1foo = filename:join(DirName1, "foo.fil"), + Name2foo = filename:join(DirName2, "foo.fil"), + Name2bar = filename:join(DirName2, "bar.dir"), + ok = ?FILE_MODULE:make_dir(DirName1), %% The name has to include the full file name, path in not enough - ?line expect({error, eisdir}, {error, eexist}, - ?FILE_MODULE:rename(Name2,DirName1)), - ?line ok = ?FILE_MODULE:rename(Name2, Name1foo), + expect({error, eisdir}, {error, eexist}, + ?FILE_MODULE:rename(Name2,DirName1)), + ok = ?FILE_MODULE:rename(Name2, Name1foo), %% Now rename the directory - ?line ok = ?FILE_MODULE:rename(DirName1,DirName2), + ok = ?FILE_MODULE:rename(DirName1,DirName2), %% And check that the file is there now - ?line {ok,Fd3} = ?FILE_MODULE:open(Name2foo, read), - ?line ok = ?FILE_MODULE:close(Fd3), + {ok,Fd3} = ?FILE_MODULE:open(Name2foo, read), + ok = ?FILE_MODULE:close(Fd3), %% Try some dirty things now: move the directory into itself - ?line {error, Msg1} = ?FILE_MODULE:rename(DirName2, Name2bar), - ?line io:format("Errmsg1: ~p",[Msg1]), + {error, Msg1} = ?FILE_MODULE:rename(DirName2, Name2bar), + io:format("Errmsg1: ~p",[Msg1]), %% move dir into a file in itself - ?line {error, Msg2} = ?FILE_MODULE:rename(DirName2, Name2foo), - ?line io:format("Errmsg2: ~p",[Msg2]), + {error, Msg2} = ?FILE_MODULE:rename(DirName2, Name2foo), + io:format("Errmsg2: ~p",[Msg2]), - ?line [] = flush(), - ?line test_server:timetrap_cancel(Dog), + [] = flush(), ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -names(suite) -> []; -names(doc) -> []; names(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(5)), - ?line RootDir = ?config(priv_dir,Config), - ?line FileName = "foo1.fil", - ?line Name1 = filename:join(RootDir, FileName), - ?line Name2 = [RootDir,"/","foo1",".","fil"], - ?line Name3 = [RootDir,"/",foo,$1,[[[],[],'.']],"f",il], - ?line {ok,Fd0} = ?FILE_MODULE:open(Name1,write), - ?line ok = ?FILE_MODULE:close(Fd0), + RootDir = proplists:get_value(priv_dir,Config), + FileName = "foo1.fil", + Name1 = filename:join(RootDir, FileName), + Name2 = [RootDir,"/","foo1",".","fil"], + Name3 = [RootDir,"/",foo,$1,[[[],[],'.']],"f",il], + {ok,Fd0} = ?FILE_MODULE:open(Name1,write), + ok = ?FILE_MODULE:close(Fd0), %% Try some file names - ?line {ok,Fd1} = ?FILE_MODULE:open(Name1,read), - ?line ok = ?FILE_MODULE:close(Fd1), - ?line {ok,Fd2f} = ?FILE_MODULE:open(lists:flatten(Name2),read), - ?line ok = ?FILE_MODULE:close(Fd2f), - ?line {ok,Fd2} = ?FILE_MODULE:open(Name2,read), - ?line ok = ?FILE_MODULE:close(Fd2), - ?line {ok,Fd3} = ?FILE_MODULE:open(Name3,read), - ?line ok = ?FILE_MODULE:close(Fd3), - case length(Name1) > 255 of - true -> - io:format("Path too long for an atom:\n\n~p\n", [Name1]); - false -> - Name4 = list_to_atom(Name1), - {ok,Fd4} = ?FILE_MODULE:open(Name4,read), - ok = ?FILE_MODULE:close(Fd4) - end, + {ok,Fd1} = ?FILE_MODULE:open(Name1,read), + ok = ?FILE_MODULE:close(Fd1), + {ok,Fd2f} = ?FILE_MODULE:open(lists:flatten(Name2),read), + ok = ?FILE_MODULE:close(Fd2f), + {ok,Fd2} = ?FILE_MODULE:open(Name2,read), + ok = ?FILE_MODULE:close(Fd2), + {ok,Fd3} = ?FILE_MODULE:open(Name3,read), + ok = ?FILE_MODULE:close(Fd3), + + %% Now try the same on raw files. + {ok,Fd4} = ?FILE_MODULE:open(Name2, [read, raw]), + ok = ?FILE_MODULE:close(Fd4), + {ok,Fd4f} = ?FILE_MODULE:open(lists:flatten(Name2), [read, raw]), + ok = ?FILE_MODULE:close(Fd4f), + {ok,Fd5} = ?FILE_MODULE:open(Name3, [read, raw]), + ok = ?FILE_MODULE:close(Fd5), + + case length(Name1) > 255 of + true -> + io:format("Path too long for an atom:\n\n~p\n", [Name1]); + false -> + Name4 = list_to_atom(Name1), + {ok,Fd6} = ?FILE_MODULE:open(Name4,read), + ok = ?FILE_MODULE:close(Fd6) + end, %% Try some path names - ?line Path1 = RootDir, - ?line Path2 = [RootDir], - ?line Path3 = ['',[],[RootDir,[[]]]], - ?line {ok,Fd11,_} = ?FILE_MODULE:path_open([Path1],FileName,read), - ?line ok = ?FILE_MODULE:close(Fd11), - ?line {ok,Fd12,_} = ?FILE_MODULE:path_open([Path2],FileName,read), - ?line ok = ?FILE_MODULE:close(Fd12), - ?line {ok,Fd13,_} = ?FILE_MODULE:path_open([Path3],FileName,read), - ?line ok = ?FILE_MODULE:close(Fd13), - case length(Path1) > 255 of - true-> - io:format("Path too long for an atom:\n\n~p\n", [Path1]); - false -> - Path4 = list_to_atom(Path1), - {ok,Fd14,_} = ?FILE_MODULE:path_open([Path4],FileName,read), - ok = ?FILE_MODULE:close(Fd14) - end, - ?line [] = flush(), - ?line test_server:timetrap_cancel(Dog), + Path1 = RootDir, + Path2 = [RootDir], + Path3 = ['',[],[RootDir,[[]]]], + {ok,Fd11,_} = ?FILE_MODULE:path_open([Path1],FileName,read), + ok = ?FILE_MODULE:close(Fd11), + {ok,Fd12,_} = ?FILE_MODULE:path_open([Path2],FileName,read), + ok = ?FILE_MODULE:close(Fd12), + {ok,Fd13,_} = ?FILE_MODULE:path_open([Path3],FileName,read), + ok = ?FILE_MODULE:close(Fd13), + case length(Path1) > 255 of + true-> + io:format("Path too long for an atom:\n\n~p\n", [Path1]); + false -> + Path4 = list_to_atom(Path1), + {ok,Fd14,_} = ?FILE_MODULE:path_open([Path4],FileName,read), + ok = ?FILE_MODULE:close(Fd14) + end, + [] = flush(), ok. +volume_relative_paths(Config) when is_list(Config) -> + case os:type() of + {win32, _} -> + {ok, [Drive, $: | _]} = file:get_cwd(), + %% Relative to current device root. + {ok, RootInfo} = file:read_file_info([Drive, $:, $/]), + {ok, RootInfo} = file:read_file_info("/"), + %% Relative to current device directory. + {ok, DirContents} = file:list_dir([Drive, $:]), + {ok, DirContents} = file:list_dir("."), + [] = flush(), + ok; + _ -> + {skip, "This test is Windows-specific."} + end. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -e_delete(suite) -> []; -e_delete(doc) -> []; e_delete(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(10)), - ?line RootDir = ?config(priv_dir, Config), - ?line Base = filename:join(RootDir, - atom_to_list(?MODULE)++"_e_delete"), - ?line ok = ?FILE_MODULE:make_dir(Base), + RootDir = proplists:get_value(priv_dir, Config), + Base = filename:join(RootDir, + atom_to_list(?MODULE)++"_e_delete"), + ok = ?FILE_MODULE:make_dir(Base), %% Delete a non-existing file. - ?line {error, enoent} = + {error, enoent} = ?FILE_MODULE:delete(filename:join(Base, "non_existing")), %% Delete a directory. - ?line {error, eperm} = ?FILE_MODULE:delete(Base), + {error, eperm} = ?FILE_MODULE:delete(Base), %% Use a path-name with a non-directory component. - ?line Afile = filename:join(Base, "a_file"), - ?line ok = ?FILE_MODULE:write_file(Afile, "hello\n"), - ?line {error, E} = + Afile = filename:join(Base, "a_file"), + ok = ?FILE_MODULE:write_file(Afile, "hello\n"), + {error, E} = expect({error, enotdir}, {error, enoent}, ?FILE_MODULE:delete(filename:join(Afile, "another_file"))), - ?line io:format("Result: ~p~n", [E]), + io:format("Result: ~p~n", [E]), %% No permission. - ?line case os:type() of - {win32, _} -> - %% Remove a character device. - ?line {error, eacces} = ?FILE_MODULE:delete("nul"); - _ -> - ?line ?FILE_MODULE:write_file_info( - Base, #file_info {mode=0}), - ?line {error, eacces} = ?FILE_MODULE:delete(Afile), - ?line ?FILE_MODULE:write_file_info( - Base, #file_info {mode=8#600}) - end, + case os:type() of + {win32, _} -> + %% Remove a character device. + {error, eacces} = ?FILE_MODULE:delete("nul"); + _ -> + ?FILE_MODULE:write_file_info( + Base, #file_info {mode=0}), + {error, eacces} = ?FILE_MODULE:delete(Afile), + ?FILE_MODULE:write_file_info( + Base, #file_info {mode=8#700}) + end, - ?line [] = flush(), - ?line test_server:timetrap_cancel(Dog), + [] = flush(), ok. %%% FreeBSD gives EEXIST when renaming a file to an empty dir, although the @@ -2105,13 +2227,10 @@ e_delete(Config) when is_list(Config) -> %%% (What about FreeBSD? We store our nightly build results on a FreeBSD %%% file system, that's what.) -e_rename(suite) -> []; -e_rename(doc) -> []; e_rename(Config) when is_list(Config) -> - Dog = test_server:timetrap(test_server:seconds(10)), - RootDir = ?config(priv_dir, Config), + RootDir = proplists:get_value(priv_dir, Config), Base = filename:join(RootDir, - atom_to_list(?MODULE)++"_e_rename"), + atom_to_list(?MODULE)++"_e_rename"), ok = ?FILE_MODULE:make_dir(Base), %% Create an empty directory. @@ -2122,15 +2241,15 @@ e_rename(Config) when is_list(Config) -> NonEmptyDir = filename:join(Base, "non_empty_dir"), ok = ?FILE_MODULE:make_dir(NonEmptyDir), ok = ?FILE_MODULE:write_file( - filename:join(NonEmptyDir, "a_file"), - "hello\n"), + filename:join(NonEmptyDir, "a_file"), + "hello\n"), %% Create another non-empty directory. ADirectory = filename:join(Base, "a_directory"), ok = ?FILE_MODULE:make_dir(ADirectory), ok = ?FILE_MODULE:write_file( - filename:join(ADirectory, "a_file"), - "howdy\n\n"), + filename:join(ADirectory, "a_file"), + "howdy\n\n"), %% Create a data file. File = filename:join(Base, "just_a_file"), @@ -2144,15 +2263,15 @@ e_rename(Config) when is_list(Config) -> %% Move Base into Base/new_name. {error, einval} = - ?FILE_MODULE:rename(Base, filename:join(Base, "new_name")), + ?FILE_MODULE:rename(Base, filename:join(Base, "new_name")), %% Overwrite a directory with a file. expect({error, eexist}, %FreeBSD (?) - {error, eisdir}, - ?FILE_MODULE:rename(File, EmptyDir)), + {error, eisdir}, + ?FILE_MODULE:rename(File, EmptyDir)), expect({error, eexist}, %FreeBSD (?) - {error, eisdir}, - ?FILE_MODULE:rename(File, NonEmptyDir)), + {error, eisdir}, + ?FILE_MODULE:rename(File, NonEmptyDir)), %% Move a non-existing file. NonExistingFile = filename:join(Base, "non_existing_file"), @@ -2160,8 +2279,8 @@ e_rename(Config) when is_list(Config) -> %% Overwrite a file with a directory. expect({error, eexist}, %FreeBSD (?) - {error, enotdir}, - ?FILE_MODULE:rename(ADirectory, File)), + {error, enotdir}, + ?FILE_MODULE:rename(ADirectory, File)), %% Move a file to another filesystem. %% XXX - This test case is bogus. We cannot be guaranteed that @@ -2170,49 +2289,42 @@ e_rename(Config) when is_list(Config) -> %% %% XXX - Gross hack! Comment = case os:type() of - {unix, _} -> - OtherFs = "/tmp", - NameOnOtherFs = filename:join(OtherFs, filename:basename(File)), - {ok, Com} = case ?FILE_MODULE:rename(File, NameOnOtherFs) of - {error, exdev} -> - %% The file could be in - %% the same filesystem! - {ok, ok}; - ok -> - {ok, {comment, - "Moving between filesystems " - "suceeded, files are probably " - "in the same filesystem!"}}; - {error, eperm} -> - {ok, {comment, "SBS! You don't " - "have the permission to do " - "this test!"}}; - Else -> - Else - end, - Com; - {win32, _} -> - %% At least Windows NT can - %% successfully move a file to - %% another drive. - ok; - {ose, _} -> - %% disabled for now - ok - end, + {unix, _} -> + OtherFs = "/tmp", + NameOnOtherFs = filename:join(OtherFs, filename:basename(File)), + {ok, Com} = case ?FILE_MODULE:rename(File, NameOnOtherFs) of + {error, exdev} -> + %% The file could be in + %% the same filesystem! + {ok, ok}; + ok -> + {ok, {comment, + "Moving between filesystems " + "suceeded, files are probably " + "in the same filesystem!"}}; + {error, eperm} -> + {ok, {comment, "SBS! You don't " + "have the permission to do " + "this test!"}}; + Else -> + Else + end, + Com; + {win32, _} -> + %% At least Windows NT can + %% successfully move a file to + %% another drive. + ok + end, [] = flush(), - test_server:timetrap_cancel(Dog), Comment. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -e_make_dir(suite) -> []; -e_make_dir(doc) -> []; e_make_dir(Config) when is_list(Config) -> - Dog = test_server:timetrap(test_server:seconds(10)), - RootDir = ?config(priv_dir, Config), + RootDir = proplists:get_value(priv_dir, Config), Base = filename:join(RootDir, - atom_to_list(?MODULE)++"_e_make_dir"), + atom_to_list(?MODULE)++"_e_make_dir"), ok = ?FILE_MODULE:make_dir(Base), %% A component of the path does not exist. @@ -2235,18 +2347,14 @@ e_make_dir(Config) when is_list(Config) -> ?FILE_MODULE:write_file_info(Base, #file_info {mode=0}), {error, eacces} = ?FILE_MODULE:make_dir(filename:join(Base, "xxxx")), ?FILE_MODULE:write_file_info( - Base, #file_info {mode=8#600}) + Base, #file_info {mode=8#700}) end, - test_server:timetrap_cancel(Dog), ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -e_del_dir(suite) -> []; -e_del_dir(doc) -> []; e_del_dir(Config) when is_list(Config) -> - Dog = test_server:timetrap(test_server:seconds(10)), - RootDir = ?config(priv_dir, Config), + RootDir = proplists:get_value(priv_dir, Config), Base = test_server:temp_name(filename:join(RootDir, "e_del_dir")), io:format("Base: ~p", [Base]), ok = ?FILE_MODULE:make_dir(Base), @@ -2259,21 +2367,21 @@ e_del_dir(Config) when is_list(Config) -> Afile = filename:join(Base, "a_directory"), ok = ?FILE_MODULE:write_file(Afile, "hello\n"), {error, E1} = expect({error, enotdir}, {error, enoent}, - ?FILE_MODULE:del_dir( - filename:join(Afile, "another_directory"))), + ?FILE_MODULE:del_dir( + filename:join(Afile, "another_directory"))), io:format("Result: ~p", [E1]), %% Delete a non-empty directory. {error, E2} = expect({error, enotempty}, {error, eexist}, {error, eacces}, - ?FILE_MODULE:del_dir(Base)), + ?FILE_MODULE:del_dir(Base)), io:format("Result: ~p", [E2]), %% Remove the current directory. {error, E3} = expect({error, einval}, - {error, eperm}, % Linux and DUX - {error, eacces}, - {error, ebusy}, - ?FILE_MODULE:del_dir(".")), + {error, eperm}, % Linux and DUX + {error, eacces}, + {error, ebusy}, + ?FILE_MODULE:del_dir(".")), io:format("Result: ~p", [E3]), %% No permission. @@ -2285,10 +2393,9 @@ e_del_dir(Config) when is_list(Config) -> ok = ?FILE_MODULE:make_dir(ADirectory), ?FILE_MODULE:write_file_info( Base, #file_info {mode=0}), {error, eacces} = ?FILE_MODULE:del_dir(ADirectory), - ?FILE_MODULE:write_file_info( Base, #file_info {mode=8#600}) + ?FILE_MODULE:write_file_info( Base, #file_info {mode=8#700}) end, [] = flush(), - test_server:timetrap_cancel(Dog), ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -2297,35 +2404,35 @@ e_del_dir(Config) when is_list(Config) -> %% Trying reading and positioning from a compressed file. read_compressed_cooked(Config) when is_list(Config) -> - ?line Data = ?config(data_dir, Config), - ?line Real = filename:join(Data, "realmen.html.gz"), - ?line {ok, Fd} = ?FILE_MODULE:open(Real, [read,compressed]), - ?line try_read_file_list(Fd). + Data = proplists:get_value(data_dir, Config), + Real = filename:join(Data, "realmen.html.gz"), + {ok, Fd} = ?FILE_MODULE:open(Real, [read,compressed]), + try_read_file_list(Fd). read_compressed_cooked_binary(Config) when is_list(Config) -> - ?line Data = ?config(data_dir, Config), - ?line Real = filename:join(Data, "realmen.html.gz"), - ?line {ok, Fd} = ?FILE_MODULE:open(Real, [read,compressed,binary]), - ?line try_read_file_binary(Fd). + Data = proplists:get_value(data_dir, Config), + Real = filename:join(Data, "realmen.html.gz"), + {ok, Fd} = ?FILE_MODULE:open(Real, [read,compressed,binary]), + try_read_file_binary(Fd). %% Trying reading and positioning from an uncompressed file, %% but with the compressed flag given. read_not_really_compressed(Config) when is_list(Config) -> - ?line Data = ?config(data_dir, Config), - ?line Priv = ?config(priv_dir, Config), + Data = proplists:get_value(data_dir, Config), + Priv = proplists:get_value(priv_dir, Config), %% The file realmen.html might have got CRs added (by WinZip). %% Remove them, or the file positions will not be correct. - ?line Real = filename:join(Data, "realmen.html"), - ?line RealPriv = filename:join(Priv, - atom_to_list(?MODULE)++"_realmen.html"), - ?line {ok, RealDataBin} = ?FILE_MODULE:read_file(Real), - ?line RealData = remove_crs(binary_to_list(RealDataBin), []), - ?line ok = ?FILE_MODULE:write_file(RealPriv, RealData), - ?line {ok, Fd} = ?FILE_MODULE:open(RealPriv, [read, compressed]), - ?line try_read_file_list(Fd). + Real = filename:join(Data, "realmen.html"), + RealPriv = filename:join(Priv, + atom_to_list(?MODULE)++"_realmen.html"), + {ok, RealDataBin} = ?FILE_MODULE:read_file(Real), + RealData = remove_crs(binary_to_list(RealDataBin), []), + ok = ?FILE_MODULE:write_file(RealPriv, RealData), + {ok, Fd} = ?FILE_MODULE:open(RealPriv, [read, compressed]), + try_read_file_list(Fd). remove_crs([$\r|Rest], Result) -> remove_crs(Rest, Result); @@ -2335,146 +2442,134 @@ remove_crs([], Result) -> lists:reverse(Result). try_read_file_list(Fd) -> - ?line Dog = test_server:timetrap(test_server:seconds(10)), - %% Seek to the current position (nothing should happen). - ?line {ok, 0} = ?FILE_MODULE:position(Fd, 0), - ?line {ok, 0} = ?FILE_MODULE:position(Fd, {cur, 0}), + {ok, 0} = ?FILE_MODULE:position(Fd, 0), + {ok, 0} = ?FILE_MODULE:position(Fd, {cur, 0}), %% Read a few lines from a compressed file. - ?line ShouldBe = "<TITLE>Real Programmers Don't Use PASCAL</TITLE>\n", - ?line ShouldBe = io:get_line(Fd, ''), + ShouldBe = "<TITLE>Real Programmers Don't Use PASCAL</TITLE>\n", + ShouldBe = io:get_line(Fd, ''), %% Now seek forward. - ?line {ok, 381} = ?FILE_MODULE:position(Fd, 381), - ?line Back = "Back in the good old days -- the \"Golden Era\" " ++ + {ok, 381} = ?FILE_MODULE:position(Fd, 381), + Back = "Back in the good old days -- the \"Golden Era\" " ++ "of computers, it was\n", - ?line Back = io:get_line(Fd, ''), + Back = io:get_line(Fd, ''), %% Try to search forward relative to the current position. - ?line {ok, CurPos} = ?FILE_MODULE:position(Fd, {cur, 0}), - ?line RealPos = 4273, - ?line {ok, RealPos} = ?FILE_MODULE:position(Fd, {cur, RealPos-CurPos}), - ?line RealProg = "<LI> Real Programmers aren't afraid to use GOTOs.\n", - ?line RealProg = io:get_line(Fd, ''), + {ok, CurPos} = ?FILE_MODULE:position(Fd, {cur, 0}), + RealPos = 4273, + {ok, RealPos} = ?FILE_MODULE:position(Fd, {cur, RealPos-CurPos}), + RealProg = "<LI> Real Programmers aren't afraid to use GOTOs.\n", + RealProg = io:get_line(Fd, ''), %% Seek backward. - ?line AfterTitle = length("<TITLE>"), - ?line {ok, AfterTitle} = ?FILE_MODULE:position(Fd, AfterTitle), - ?line Title = "Real Programmers Don't Use PASCAL</TITLE>\n", - ?line Title = io:get_line(Fd, ''), + AfterTitle = length("<TITLE>"), + {ok, AfterTitle} = ?FILE_MODULE:position(Fd, AfterTitle), + Title = "Real Programmers Don't Use PASCAL</TITLE>\n", + Title = io:get_line(Fd, ''), %% Seek past the end of the file. - ?line {ok, _} = ?FILE_MODULE:position(Fd, 25000), + {ok, _} = ?FILE_MODULE:position(Fd, 25000), %% Done. - ?line ?FILE_MODULE:close(Fd), - ?line [] = flush(), - ?line test_server:timetrap_cancel(Dog), + ?FILE_MODULE:close(Fd), + [] = flush(), ok. try_read_file_binary(Fd) -> - ?line Dog = test_server:timetrap(test_server:seconds(10)), - %% Seek to the current position (nothing should happen). - ?line {ok, 0} = ?FILE_MODULE:position(Fd, 0), - ?line {ok, 0} = ?FILE_MODULE:position(Fd, {cur, 0}), + {ok, 0} = ?FILE_MODULE:position(Fd, 0), + {ok, 0} = ?FILE_MODULE:position(Fd, {cur, 0}), %% Read a few lines from a compressed file. - ?line ShouldBe = <<"<TITLE>Real Programmers Don't Use PASCAL</TITLE>\n">>, - ?line ShouldBe = io:get_line(Fd, ''), + ShouldBe = <<"<TITLE>Real Programmers Don't Use PASCAL</TITLE>\n">>, + ShouldBe = io:get_line(Fd, ''), %% Now seek forward. - ?line {ok, 381} = ?FILE_MODULE:position(Fd, 381), - ?line Back = <<"Back in the good old days -- the \"Golden Era\" " - "of computers, it was\n">>, - ?line Back = io:get_line(Fd, ''), + {ok, 381} = ?FILE_MODULE:position(Fd, 381), + Back = <<"Back in the good old days -- the \"Golden Era\" " + "of computers, it was\n">>, + Back = io:get_line(Fd, ''), %% Try to search forward relative to the current position. - ?line {ok, CurPos} = ?FILE_MODULE:position(Fd, {cur, 0}), - ?line RealPos = 4273, - ?line {ok, RealPos} = ?FILE_MODULE:position(Fd, {cur, RealPos-CurPos}), - ?line RealProg = <<"<LI> Real Programmers aren't afraid to use GOTOs.\n">>, - ?line RealProg = io:get_line(Fd, ''), + {ok, CurPos} = ?FILE_MODULE:position(Fd, {cur, 0}), + RealPos = 4273, + {ok, RealPos} = ?FILE_MODULE:position(Fd, {cur, RealPos-CurPos}), + RealProg = <<"<LI> Real Programmers aren't afraid to use GOTOs.\n">>, + RealProg = io:get_line(Fd, ''), %% Seek backward. - ?line AfterTitle = length("<TITLE>"), - ?line {ok, AfterTitle} = ?FILE_MODULE:position(Fd, AfterTitle), - ?line Title = <<"Real Programmers Don't Use PASCAL</TITLE>\n">>, - ?line Title = io:get_line(Fd, ''), + AfterTitle = length("<TITLE>"), + {ok, AfterTitle} = ?FILE_MODULE:position(Fd, AfterTitle), + Title = <<"Real Programmers Don't Use PASCAL</TITLE>\n">>, + Title = io:get_line(Fd, ''), %% Done. - ?line ?FILE_MODULE:close(Fd), - ?line [] = flush(), - ?line test_server:timetrap_cancel(Dog), + ?FILE_MODULE:close(Fd), + [] = flush(), ok. read_cooked_tar_problem(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(10)), + Data = proplists:get_value(data_dir, Config), + ProblemFile = filename:join(Data, "cooked_tar_problem.tar.gz"), + {ok,Fd} = ?FILE_MODULE:open(ProblemFile, [read,compressed,binary]), - ?line Data = ?config(data_dir, Config), - ?line ProblemFile = filename:join(Data, "cooked_tar_problem.tar.gz"), - ?line {ok,Fd} = ?FILE_MODULE:open(ProblemFile, [read,compressed,binary]), + {ok,34304} = file:position(Fd, 34304), + {ok,Bin} = file:read(Fd, 512), + 512 = byte_size(Bin), - ?line {ok,34304} = file:position(Fd, 34304), - ?line {ok,Bin} = file:read(Fd, 512), - ?line 512 = byte_size(Bin), - - ?line {ok,34304+512+1024} = file:position(Fd, {cur,1024}), - - ?line ok = file:close(Fd), + {ok,34304+512+1024} = file:position(Fd, {cur,1024}), + + ok = file:close(Fd), - ?line test_server:timetrap_cancel(Dog), ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -write_compressed(suite) -> []; -write_compressed(doc) -> []; write_compressed(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(10)), - ?line Priv = ?config(priv_dir, Config), - ?line MyFile = filename:join(Priv, - atom_to_list(?MODULE)++"_test.gz"), + Priv = proplists:get_value(priv_dir, Config), + MyFile = filename:join(Priv, + atom_to_list(?MODULE)++"_test.gz"), %% Write a file. - ?line {ok, Fd} = ?FILE_MODULE:open(MyFile, [write, compressed]), - ?line {ok, 0} = ?FILE_MODULE:position(Fd, 0), - ?line Prefix = "hello\n", - ?line End = "end\n", - ?line ok = io:put_chars(Fd, Prefix), - ?line {ok, 143} = ?FILE_MODULE:position(Fd, 143), - ?line ok = io:put_chars(Fd, End), - ?line ok = ?FILE_MODULE:close(Fd), + {ok, Fd} = ?FILE_MODULE:open(MyFile, [write, compressed]), + {ok, 0} = ?FILE_MODULE:position(Fd, 0), + Prefix = "hello\n", + End = "end\n", + ok = io:put_chars(Fd, Prefix), + {ok, 143} = ?FILE_MODULE:position(Fd, 143), + ok = io:put_chars(Fd, End), + ok = ?FILE_MODULE:close(Fd), %% Read the file and verify the contents. - ?line {ok, Fd1} = ?FILE_MODULE:open(MyFile, [read, compressed]), - ?line Prefix = io:get_line(Fd1, ''), - ?line Second = lists:duplicate(143-length(Prefix), 0) ++ End, - ?line Second = io:get_line(Fd1, ''), - ?line ok = ?FILE_MODULE:close(Fd1), + {ok, Fd1} = ?FILE_MODULE:open(MyFile, [read, compressed]), + Prefix = io:get_line(Fd1, ''), + Second = lists:duplicate(143-length(Prefix), 0) ++ End, + Second = io:get_line(Fd1, ''), + ok = ?FILE_MODULE:close(Fd1), %% Verify successful compression by uncompressing the file %% using zlib:gunzip/1. - ?line {ok,Contents} = file:read_file(MyFile), - ?line <<"hello\n",0:137/unit:8,"end\n">> = zlib:gunzip(Contents), + {ok,Contents} = file:read_file(MyFile), + <<"hello\n",0:137/unit:8,"end\n">> = zlib:gunzip(Contents), %% Ensure that the file is compressed. @@ -2483,99 +2578,92 @@ write_compressed(Config) when is_list(Config) -> {ok, #file_info{size=Size}} when Size < TotalSize -> ok; {ok, #file_info{size=Size}} when Size == TotalSize -> - test_server:fail(file_not_compressed) + ct:fail(file_not_compressed) end, %% Write again to ensure that the file is truncated. - ?line {ok, Fd2} = ?FILE_MODULE:open(MyFile, [write, compressed]), - ?line NewString = "aaaaaaaaaaa", - ?line ok = io:put_chars(Fd2, NewString), - ?line ok = ?FILE_MODULE:close(Fd2), - ?line {ok, Fd3} = ?FILE_MODULE:open(MyFile, [read, compressed]), - ?line {ok, NewString} = ?FILE_MODULE:read(Fd3, 1024), - ?line ok = ?FILE_MODULE:close(Fd3), + {ok, Fd2} = ?FILE_MODULE:open(MyFile, [write, compressed]), + NewString = "aaaaaaaaaaa", + ok = io:put_chars(Fd2, NewString), + ok = ?FILE_MODULE:close(Fd2), + {ok, Fd3} = ?FILE_MODULE:open(MyFile, [read, compressed]), + {ok, NewString} = ?FILE_MODULE:read(Fd3, 1024), + ok = ?FILE_MODULE:close(Fd3), %% Done. - ?line [] = flush(), - ?line test_server:timetrap_cancel(Dog), + [] = flush(), ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% catenated_gzips(Config) when is_list(Config) -> - ?line Priv = ?config(priv_dir, Config), - ?line MyFile = filename:join(Priv, ?MODULE_STRING++"_test.gz"), + Priv = proplists:get_value(priv_dir, Config), + MyFile = filename:join(Priv, ?MODULE_STRING++"_test.gz"), First = "Hello, all good men going to search parties. ", Second = "Now I really need your help.", All = iolist_to_binary([First|Second]), - ?line Cat = [zlib:gzip(First),zlib:gzip(Second)], - - ?line ok = file:write_file(MyFile, Cat), + Cat = [zlib:gzip(First),zlib:gzip(Second)], - ?line {ok,Fd} = file:open(MyFile, [read,compressed,binary]), - ?line {ok,All} = file:read(Fd, 100000), - ?line ok = file:close(Fd), + ok = file:write_file(MyFile, Cat), + + {ok,Fd} = file:open(MyFile, [read,compressed,binary]), + {ok,All} = file:read(Fd, 100000), + ok = file:close(Fd), ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -compress_errors(suite) -> []; -compress_errors(doc) -> []; compress_errors(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(10)), - ?line DataDir = + DataDir = filename:dirname( - filename:join(?config(data_dir, Config), "x")), - ?line DataDirSlash = DataDir++"/", - ?line {error, enoent} = ?FILE_MODULE:open("non_existing__", - [compressed, read]), - ?line {error, einval} = ?FILE_MODULE:open("non_existing__", - [compressed, read, write]), - ?line {error, einval} = ?FILE_MODULE:open("non_existing__", - [compressed, read, append]), - ?line {error, einval} = ?FILE_MODULE:open("non_existing__", - [compressed, write, append]), - ?line {error, E1} = ?FILE_MODULE:open(DataDir, [compressed, read]), - ?line {error, E2} = ?FILE_MODULE:open(DataDirSlash, [compressed, read]), - ?line {error, E3} = ?FILE_MODULE:open(DataDir, [compressed, write]), - ?line {error, E4} = ?FILE_MODULE:open(DataDirSlash, [compressed, write]), - ?line {eisdir,eisdir,eisdir,eisdir} = {E1,E2,E3,E4}, + filename:join(proplists:get_value(data_dir, Config), "x")), + DataDirSlash = DataDir++"/", + {error, enoent} = ?FILE_MODULE:open("non_existing__", + [compressed, read]), + {error, einval} = ?FILE_MODULE:open("non_existing__", + [compressed, read, write]), + {error, einval} = ?FILE_MODULE:open("non_existing__", + [compressed, read, append]), + {error, einval} = ?FILE_MODULE:open("non_existing__", + [compressed, write, append]), + {error, E1} = ?FILE_MODULE:open(DataDir, [compressed, read]), + {error, E2} = ?FILE_MODULE:open(DataDirSlash, [compressed, read]), + {error, E3} = ?FILE_MODULE:open(DataDir, [compressed, write]), + {error, E4} = ?FILE_MODULE:open(DataDirSlash, [compressed, write]), + {eisdir,eisdir,eisdir,eisdir} = {E1,E2,E3,E4}, %% Read a corrupted .gz file. - ?line Corrupted = filename:join(DataDir, "corrupted.gz"), - ?line {ok, Fd} = ?FILE_MODULE:open(Corrupted, [read, compressed]), - ?line {error, eio} = ?FILE_MODULE:read(Fd, 100), - ?line ?FILE_MODULE:close(Fd), + Corrupted = filename:join(DataDir, "corrupted.gz"), + {ok, Fd} = ?FILE_MODULE:open(Corrupted, [read, compressed]), + {error, eio} = ?FILE_MODULE:read(Fd, 100), + ?FILE_MODULE:close(Fd), - ?line [] = flush(), - ?line test_server:timetrap_cancel(Dog), + [] = flush(), ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -compress_async_crash(suite) -> []; -compress_async_crash(doc) -> []; compress_async_crash(Config) when is_list(Config) -> - ?line DataDir = ?config(data_dir, Config), - ?line Path = filename:join(DataDir, "test.gz"), + DataDir = proplists:get_value(data_dir, Config), + Path = filename:join(DataDir, "test.gz"), ExpectedData = <<"qwerty">>, - ?line _ = ?FILE_MODULE:delete(Path), - ?line {ok, Fd} = ?FILE_MODULE:open(Path, [write, binary, compressed]), - ?line ok = ?FILE_MODULE:write(Fd, ExpectedData), - ?line ok = ?FILE_MODULE:close(Fd), + _ = ?FILE_MODULE:delete(Path), + {ok, Fd} = ?FILE_MODULE:open(Path, [write, binary, compressed]), + ok = ?FILE_MODULE:write(Fd, ExpectedData), + ok = ?FILE_MODULE:close(Fd), - % Test that when using async thread pool, the emulator doesn't crash - % when the efile port driver is stopped while a compressed file operation - % is in progress (being carried by an async thread). - ?line ok = compress_async_crash_loop(10000, Path, ExpectedData), - ?line ok = ?FILE_MODULE:delete(Path), + %% Test that when using async thread pool, the emulator doesn't crash + %% when the efile port driver is stopped while a compressed file operation + %% is in progress (being carried by an async thread). + ok = compress_async_crash_loop(10000, Path, ExpectedData), + ok = ?FILE_MODULE:delete(Path), ok. compress_async_crash_loop(0, _Path, _ExpectedData) -> @@ -2583,80 +2671,107 @@ compress_async_crash_loop(0, _Path, _ExpectedData) -> compress_async_crash_loop(N, Path, ExpectedData) -> Parent = self(), {Pid, Ref} = spawn_monitor( - fun() -> - ?line {ok, Fd} = ?FILE_MODULE:open( - Path, [read, compressed, raw, binary]), - Len = byte_size(ExpectedData), - Parent ! {self(), continue}, - ?line {ok, ExpectedData} = ?FILE_MODULE:read(Fd, Len), - ?line ok = ?FILE_MODULE:close(Fd), - receive foobar -> ok end - end), + fun() -> + {ok, Fd} = ?FILE_MODULE:open( + Path, [read, compressed, raw, binary]), + Len = byte_size(ExpectedData), + Parent ! {self(), continue}, + {ok, ExpectedData} = ?FILE_MODULE:read(Fd, Len), + ok = ?FILE_MODULE:close(Fd), + receive foobar -> ok end + end), receive {Pid, continue} -> exit(Pid, shutdown), receive {'DOWN', Ref, _, _, Reason} -> - ?line shutdown = Reason + shutdown = Reason end; {'DOWN', Ref, _, _, Reason2} -> - test_server:fail({worker_exited, Reason2}) + ct:fail({worker_exited, Reason2}) after 60000 -> exit(Pid, shutdown), erlang:demonitor(Ref, [flush]), - test_server:fail(worker_timeout) + ct:fail(worker_timeout) end, compress_async_crash_loop(N - 1, Path, ExpectedData). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -altname(doc) -> - "Test the file:altname/1 function"; -altname(suite) -> - []; +unicode(Config) when is_list(Config) -> + Dir = proplists:get_value(priv_dir, Config), + Name = filename:join(Dir, "data-utf8.txt"), + Txt = lists:seq(128, 255), + D = unicode:characters_to_binary(Txt, latin1, latin1), + {ok,Fd1} = + ?FILE_MODULE:open(Name, [write,read,binary,{encoding,unicode}]), + ok = ?FILE_MODULE:truncate(Fd1), + ok = ?FILE_MODULE:write(Fd1, Txt), + {ok,0} = ?FILE_MODULE:position(Fd1, bof), + {ok,D} = ?FILE_MODULE:read(Fd1, 129), + {ok,0} = ?FILE_MODULE:position(Fd1, bof), + {ok,D1} = ?FILE_MODULE:read(Fd1, 64), + {ok,Pos} = ?FILE_MODULE:position(Fd1, cur), + {ok,D2} = ?FILE_MODULE:pread(Fd1, {cur,0}, 65), + D = <<D1/binary, D2/binary>>, + {ok,D1} = ?FILE_MODULE:pread(Fd1, bof, 64), + {ok,Pos} = ?FILE_MODULE:position(Fd1, Pos), + {ok,D2} = ?FILE_MODULE:read(Fd1, 64), + ok = ?FILE_MODULE:close(Fd1), + %% + RawD = unicode:characters_to_binary(Txt, latin1, unicode), + {ok,RawD} = ?FILE_MODULE:read_file(Name), + %% + {ok,Fd2} = ?FILE_MODULE:open(Name, [read,{encoding,unicode}]), + {ok,Txt} = ?FILE_MODULE:read(Fd2, 129), + {Txt1,Txt2} = lists:split(64, Txt), + {ok,Txt2} = ?FILE_MODULE:pread(Fd2, Pos, 65), + {ok,0} = ?FILE_MODULE:position(Fd2, bof), + {ok,Txt1} = ?FILE_MODULE:read(Fd2, 64), + ok = ?FILE_MODULE:close(Fd2). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% Test the file:altname/1 function. altname(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(10)), - ?line RootDir = ?config(priv_dir, Config), - ?line NewDir = filename:join(RootDir, - "long alternative path name with spaces"), - ?line ok = ?FILE_MODULE:make_dir(NewDir), - ?line Name = filename:join(NewDir, "a_file_with_long_name"), - ?line ShortName = filename:join(NewDir, "short"), - ?line NonexName = filename:join(NewDir, "nonexistent"), - ?line ok = ?FILE_MODULE:write_file(Name, "some contents\n"), - ?line ok = ?FILE_MODULE:write_file(ShortName, "some contents\n"), - ?line Result = + RootDir = proplists:get_value(priv_dir, Config), + NewDir = filename:join(RootDir, + "long alternative path name with spaces"), + ok = ?FILE_MODULE:make_dir(NewDir), + Name = filename:join(NewDir, "a_file_with_long_name"), + ShortName = filename:join(NewDir, "short"), + NonexName = filename:join(NewDir, "nonexistent"), + ok = ?FILE_MODULE:write_file(Name, "some contents\n"), + ok = ?FILE_MODULE:write_file(ShortName, "some contents\n"), + Result = case ?FILE_MODULE:altname(NewDir) of {error, enotsup} -> {skipped, "Altname not supported on this platform"}; {ok, "LONGAL~1"} -> - ?line {ok, "A_FILE~1"} = ?FILE_MODULE:altname(Name), - ?line {ok, "C:/"} = ?FILE_MODULE:altname("C:/"), - ?line {ok, "C:\\"} = ?FILE_MODULE:altname("C:\\"), - ?line {error,enoent} = ?FILE_MODULE:altname(NonexName), - ?line {ok, "short"} = ?FILE_MODULE:altname(ShortName), + {ok, "A_FILE~1"} = ?FILE_MODULE:altname(Name), + {ok, "c:/"} = ?FILE_MODULE:altname("C:/"), + {ok, "c:/"} = ?FILE_MODULE:altname("C:\\"), + {error,enoent} = ?FILE_MODULE:altname(NonexName), + {ok, "short"} = ?FILE_MODULE:altname(ShortName), ok end, - ?line [] = flush(), - ?line test_server:timetrap_cancel(Dog), + [] = flush(), Result. -make_link(doc) -> "Test creating a hard link."; -make_link(suite) -> []; +%% Test creating a hard link. make_link(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(10)), - ?line RootDir = ?config(priv_dir, Config), - ?line NewDir = filename:join(RootDir, - atom_to_list(?MODULE) - ++"_make_link"), - ?line ok = ?FILE_MODULE:make_dir(NewDir), - - ?line Name = filename:join(NewDir, "a_file"), - ?line ok = ?FILE_MODULE:write_file(Name, "some contents\n"), - - ?line Alias = filename:join(NewDir, "an_alias"), - ?line Result = + RootDir = proplists:get_value(priv_dir, Config), + NewDir = filename:join(RootDir, + atom_to_list(?MODULE) + ++"_make_link"), + ok = ?FILE_MODULE:make_dir(NewDir), + + Name = filename:join(NewDir, "a_file"), + ok = ?FILE_MODULE:write_file(Name, "some contents\n"), + + Alias = filename:join(NewDir, "an_alias"), + Result = case ?FILE_MODULE:make_link(Name, Alias) of {error, enotsup} -> {skipped, "Links not supported on this platform"}; @@ -2666,53 +2781,45 @@ make_link(Config) when is_list(Config) -> %% which should in behave exactly as %% ?FILE_MODULE:read_file_info/1 %% since they are not used on symbolic links. - - ?line {ok, Info} = ?FILE_MODULE:read_link_info(Name), + + {ok, Info} = ?FILE_MODULE:read_link_info(Name), {ok,Info} = ?FILE_MODULE:read_link_info(Name, [raw]), - ?line {ok, Info} = ?FILE_MODULE:read_link_info(Alias), + {ok, Info} = ?FILE_MODULE:read_link_info(Alias), {ok,Info} = ?FILE_MODULE:read_link_info(Alias, [raw]), - ?line #file_info{links = 2, type = regular} = Info, - ?line {error, eexist} = + #file_info{links = 2, type = regular} = Info, + {error, eexist} = ?FILE_MODULE:make_link(Name, Alias), ok end, - - ?line [] = flush(), - ?line test_server:timetrap_cancel(Dog), + + [] = flush(), Result. -read_link_info_for_non_link(doc) -> - "Test that reading link info for an ordinary file or directory works " - "(on all platforms)."; -read_link_info_for_non_link(suite) -> []; +%% Test that reading link info for an ordinary file or directory works +%% (on all platforms). read_link_info_for_non_link(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(10)), - - ?line {ok, #file_info{type=directory}} = + {ok, #file_info{type=directory}} = ?FILE_MODULE:read_link_info("."), {ok, #file_info{type=directory}} = ?FILE_MODULE:read_link_info(".", [raw]), - - ?line [] = flush(), - ?line test_server:timetrap_cancel(Dog), + + [] = flush(), ok. -symlinks(doc) -> "Test operations on symbolic links (for Unix)."; -symlinks(suite) -> []; +%% Test operations on symbolic links (for Unix). symlinks(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(10)), - ?line {error, _} = ?FILE_MODULE:read_link(lists:duplicate(10000,$a)), + {error, _} = ?FILE_MODULE:read_link(lists:duplicate(10000,$a)), {error, _} = ?FILE_MODULE:read_link_all(lists:duplicate(10000,$a)), - ?line RootDir = ?config(priv_dir, Config), - ?line NewDir = filename:join(RootDir, - atom_to_list(?MODULE) - ++"_symlinks"), - ?line ok = ?FILE_MODULE:make_dir(NewDir), - - ?line Name = filename:join(NewDir, "a_plain_file"), - ?line ok = ?FILE_MODULE:write_file(Name, "some stupid content\n"), - - ?line Alias = filename:join(NewDir, "a_symlink_alias"), - ?line Result = + RootDir = proplists:get_value(priv_dir, Config), + NewDir = filename:join(RootDir, + atom_to_list(?MODULE) + ++"_symlinks"), + ok = ?FILE_MODULE:make_dir(NewDir), + + Name = filename:join(NewDir, "a_plain_file"), + ok = ?FILE_MODULE:write_file(Name, "some stupid content\n"), + + Alias = filename:join(NewDir, "a_symlink_alias"), + Result = case ?FILE_MODULE:make_symlink(Name, Alias) of {error, enotsup} -> {skipped, "Links not supported on this platform"}; @@ -2720,41 +2827,37 @@ symlinks(Config) when is_list(Config) -> {win32,_} = os:type(), {skipped, "Windows user not privileged to create symlinks"}; ok -> - ?line {ok, Info1} = ?FILE_MODULE:read_file_info(Name), + {ok, Info1} = ?FILE_MODULE:read_file_info(Name), {ok,Info1} = ?FILE_MODULE:read_file_info(Name, [raw]), - ?line {ok, Info1} = ?FILE_MODULE:read_file_info(Alias), + {ok, Info1} = ?FILE_MODULE:read_file_info(Alias), {ok,Info1} = ?FILE_MODULE:read_file_info(Alias, [raw]), - ?line {ok, Info1} = ?FILE_MODULE:read_link_info(Name), + {ok, Info1} = ?FILE_MODULE:read_link_info(Name), {ok,Info1} = ?FILE_MODULE:read_link_info(Name, [raw]), - ?line #file_info{links = 1, type = regular} = Info1, - - ?line {ok, Info2} = ?FILE_MODULE:read_link_info(Alias), + #file_info{links = 1, type = regular} = Info1, + + {ok, Info2} = ?FILE_MODULE:read_link_info(Alias), {ok,Info2} = ?FILE_MODULE:read_link_info(Alias, [raw]), - ?line #file_info{links=1, type=symlink} = Info2, - ?line {ok, Name} = ?FILE_MODULE:read_link(Alias), + #file_info{links=1, type=symlink} = Info2, + {ok, Name} = ?FILE_MODULE:read_link(Alias), {ok, Name} = ?FILE_MODULE:read_link_all(Alias), %% If all is good, delete dir again (avoid hanging dir on windows) rm_rf(?FILE_MODULE,NewDir), ok - end, - - ?line [] = flush(), - ?line test_server:timetrap_cancel(Dog), + end, + + [] = flush(), Result. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -copy(doc) -> []; -copy(suite) -> []; copy(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(10)), - ?line RootDir = ?config(priv_dir, Config), + RootDir = proplists:get_value(priv_dir, Config), %% Create a text file. - ?line Name1 = filename:join(RootDir, atom_to_list(?MODULE)++"_copy_1.txt"), - ?line Line = "The quick brown fox jumps over a lazy dog. 0123456789\n", - ?line Len = length(Line), - ?line {ok, Handle1} = ?FILE_MODULE:open(Name1, [write]), - ?line {_, Size1} = + Name1 = filename:join(RootDir, atom_to_list(?MODULE)++"_copy_1.txt"), + Line = "The quick brown fox jumps over a lazy dog. 0123456789\n", + Len = length(Line), + {ok, Handle1} = ?FILE_MODULE:open(Name1, [write]), + {_, Size1} = iterate({0, 0}, done, fun({_, S}) when S >= 128*1024 -> @@ -2764,45 +2867,44 @@ copy(Config) when is_list(Config) -> ok = ?FILE_MODULE:write(Handle1, [H, " ", Line]), {N + 1, S + length(H) + 1 + Len} end), - ?line ?FILE_MODULE:close(Handle1), + ?FILE_MODULE:close(Handle1), %% Make a copy - ?line Name2 = filename:join(RootDir, atom_to_list(?MODULE)++"_copy_2.txt"), - ?line {ok, Size1} = ?FILE_MODULE:copy(Name1, Name2), + Name2 = filename:join(RootDir, atom_to_list(?MODULE)++"_copy_2.txt"), + {ok, Size1} = ?FILE_MODULE:copy(Name1, Name2), %% Concatenate 1 - ?line Name3 = filename:join(RootDir, atom_to_list(?MODULE)++"_copy_3.txt"), - ?line {ok, Handle3} = ?FILE_MODULE:open(Name3, [raw, write, binary]), - ?line {ok, Size1} = ?FILE_MODULE:copy(Name1, Handle3), - ?line {ok, Handle2} = ?FILE_MODULE:open(Name2, [read, binary]), - ?line {ok, Size1} = ?FILE_MODULE:copy(Handle2, Handle3), - ?line ok = ?FILE_MODULE:close(Handle3), - ?line ok = ?FILE_MODULE:close(Handle2), + Name3 = filename:join(RootDir, atom_to_list(?MODULE)++"_copy_3.txt"), + {ok, Handle3} = ?FILE_MODULE:open(Name3, [raw, write, binary]), + {ok, Size1} = ?FILE_MODULE:copy(Name1, Handle3), + {ok, Handle2} = ?FILE_MODULE:open(Name2, [read, binary]), + {ok, Size1} = ?FILE_MODULE:copy(Handle2, Handle3), + ok = ?FILE_MODULE:close(Handle3), + ok = ?FILE_MODULE:close(Handle2), %% Concatenate 2 - ?line Name4 = filename:join(RootDir, atom_to_list(?MODULE)++"_copy_4.txt"), - ?line {ok, Handle4} = ?FILE_MODULE:open(Name4, [write, binary]), - ?line {ok, Size1} = ?FILE_MODULE:copy(Name1, Handle4), - ?line {ok, Handle5} = ?FILE_MODULE:open(Name2, [raw, read, binary]), - ?line {ok, Size1} = ?FILE_MODULE:copy(Handle5, Handle4), - ?line ok = ?FILE_MODULE:close(Handle5), - ?line ok = ?FILE_MODULE:close(Handle4), + Name4 = filename:join(RootDir, atom_to_list(?MODULE)++"_copy_4.txt"), + {ok, Handle4} = ?FILE_MODULE:open(Name4, [write, binary]), + {ok, Size1} = ?FILE_MODULE:copy(Name1, Handle4), + {ok, Handle5} = ?FILE_MODULE:open(Name2, [raw, read, binary]), + {ok, Size1} = ?FILE_MODULE:copy(Handle5, Handle4), + ok = ?FILE_MODULE:close(Handle5), + ok = ?FILE_MODULE:close(Handle4), %% %% Just for test of the test - %% ?line {ok, Handle2q} = ?FILE_MODULE:open(Name2, [write, append]), - %% ?line ok = ?FILE_MODULE:write(Handle2q, "q"), - %% ?line ok = ?FILE_MODULE:close(Handle2q), + %% {ok, Handle2q} = ?FILE_MODULE:open(Name2, [write, append]), + %% ok = ?FILE_MODULE:write(Handle2q, "q"), + %% ok = ?FILE_MODULE:close(Handle2q), %% Compare the files - ?line {ok, Handle1a} = ?FILE_MODULE:open(Name1, [raw, read]), - ?line {ok, Handle2a} = ?FILE_MODULE:open(Name2, [raw, read]), - ?line true = stream_cmp(fd_stream_factory([Handle1a]), - fd_stream_factory([Handle2a])), - ?line {ok, 0} = ?FILE_MODULE:position(Handle1a, 0), - ?line {ok, 0} = ?FILE_MODULE:position(Handle2a, 0), - ?line {ok, Handle3a} = ?FILE_MODULE:open(Name3, [raw, read]), - ?line true = stream_cmp(fd_stream_factory([Handle1a, Handle2a]), - fd_stream_factory([Handle2a])), - ?line ok = ?FILE_MODULE:close(Handle1a), - ?line ok = ?FILE_MODULE:close(Handle2a), - ?line ok = ?FILE_MODULE:close(Handle3a), - ?line [] = flush(), - ?line test_server:timetrap_cancel(Dog), + {ok, Handle1a} = ?FILE_MODULE:open(Name1, [raw, read]), + {ok, Handle2a} = ?FILE_MODULE:open(Name2, [raw, read]), + true = stream_cmp(fd_stream_factory([Handle1a]), + fd_stream_factory([Handle2a])), + {ok, 0} = ?FILE_MODULE:position(Handle1a, 0), + {ok, 0} = ?FILE_MODULE:position(Handle2a, 0), + {ok, Handle3a} = ?FILE_MODULE:open(Name3, [raw, read]), + true = stream_cmp(fd_stream_factory([Handle1a, Handle2a]), + fd_stream_factory([Handle2a])), + ok = ?FILE_MODULE:close(Handle1a), + ok = ?FILE_MODULE:close(Handle2a), + ok = ?FILE_MODULE:close(Handle3a), + [] = flush(), ok. @@ -2823,7 +2925,7 @@ fd_stream_factory([Fd | T] = L) -> end end. - + stream_cmp(F1, F2) when is_function(F1), is_function(F2) -> stream_cmp(F1(), F2()); @@ -2848,80 +2950,75 @@ stream_cmp([H | T1], [H | T2]) -> %% Test the get_cwd(), open(), and copy() file server calls. new_slave(_RootDir, Cwd) -> - ?line L = "qwertyuiopasdfghjklzxcvbnm", - ?line N = length(L), - ?line {ok, Cwd} = ?FILE_MODULE:get_cwd(), - ?line {error, enotsup} = ?FILE_MODULE:get_cwd("C:"), % Unix only testcase - ?line {ok, FD1} = ?FILE_MODULE:open("file1.txt", write), - ?line ok = ?FILE_MODULE:close(FD1), - ?line {ok, FD2} = ?FILE_MODULE:open("file1.txt", - [write, append, - binary, compressed, - delayed_write, - {delayed_write, 0, 0}, - read_ahead, - {read_ahead, 0}]), - ?line ok = ?FILE_MODULE:write(FD2, L), - ?line ok = ?FILE_MODULE:close(FD2), - ?line {ok, N2} = ?FILE_MODULE:copy("file1.txt", "file2.txt"), - ?line io:format("Size ~p, compressed ~p.~n", [N, N2]), - ?line {ok, FD3} = ?FILE_MODULE:open("file2.txt", - [binary, compressed]), + L = "qwertyuiopasdfghjklzxcvbnm", + N = length(L), + {ok, Cwd} = ?FILE_MODULE:get_cwd(), + {error, enotsup} = ?FILE_MODULE:get_cwd("C:"), % Unix only testcase + {ok, FD1} = ?FILE_MODULE:open("file1.txt", write), + ok = ?FILE_MODULE:close(FD1), + {ok, FD2} = ?FILE_MODULE:open("file1.txt", + [write, append, + binary, compressed, + delayed_write, + {delayed_write, 0, 0}, + read_ahead, + {read_ahead, 0}]), + ok = ?FILE_MODULE:write(FD2, L), + ok = ?FILE_MODULE:close(FD2), + {ok, N2} = ?FILE_MODULE:copy("file1.txt", "file2.txt"), + io:format("Size ~p, compressed ~p.~n", [N, N2]), + {ok, FD3} = ?FILE_MODULE:open("file2.txt", + [binary, compressed]), %% The file_io_server will translate the binary into a list - ?line {ok, L} = ?FILE_MODULE:read(FD3, N+1), - ?line ok = ?FILE_MODULE:close(FD3), + {ok, L} = ?FILE_MODULE:read(FD3, N+1), + ok = ?FILE_MODULE:close(FD3), %% - ?line ok = ?FILE_MODULE:delete("file1.txt"), - ?line ok = ?FILE_MODULE:delete("file2.txt"), - ?line [] = flush(), + ok = ?FILE_MODULE:delete("file1.txt"), + ok = ?FILE_MODULE:delete("file2.txt"), + [] = flush(), ok. %% Test the get_cwd() and open() file server calls. old_slave(_RootDir, Cwd) -> - ?line L = "qwertyuiopasdfghjklzxcvbnm", - ?line N = length(L), - ?line {ok, Cwd} = ?FILE_MODULE:get_cwd(), - ?line {error, enotsup} = ?FILE_MODULE:get_cwd("C:"), % Unix only testcase - ?line {ok, FD1} = ?FILE_MODULE:open("file1.txt", write), - ?line ok = ?FILE_MODULE:close(FD1), - ?line {ok, FD2} = ?FILE_MODULE:open("file1.txt", - [write, binary, compressed]), - ?line ok = ?FILE_MODULE:write(FD2, L), - ?line ok = ?FILE_MODULE:close(FD2), - ?line {ok, FD3} = ?FILE_MODULE:open("file1.txt", [write, append]), - ?line ok = ?FILE_MODULE:close(FD3), - ?line {ok, FD4} = ?FILE_MODULE:open("file1.txt", - [binary, compressed]), + L = "qwertyuiopasdfghjklzxcvbnm", + N = length(L), + {ok, Cwd} = ?FILE_MODULE:get_cwd(), + {error, enotsup} = ?FILE_MODULE:get_cwd("C:"), % Unix only testcase + {ok, FD1} = ?FILE_MODULE:open("file1.txt", write), + ok = ?FILE_MODULE:close(FD1), + {ok, FD2} = ?FILE_MODULE:open("file1.txt", + [write, binary, compressed]), + ok = ?FILE_MODULE:write(FD2, L), + ok = ?FILE_MODULE:close(FD2), + {ok, FD3} = ?FILE_MODULE:open("file1.txt", [write, append]), + ok = ?FILE_MODULE:close(FD3), + {ok, FD4} = ?FILE_MODULE:open("file1.txt", + [binary, compressed]), %% The file_io_server will translate the binary into a list - ?line {ok, L} = ?FILE_MODULE:read(FD4, N+1), - ?line ok = ?FILE_MODULE:close(FD4), + {ok, L} = ?FILE_MODULE:read(FD4, N+1), + ok = ?FILE_MODULE:close(FD4), %% - ?line ok = ?FILE_MODULE:delete("file1.txt"), - ?line [] = flush(), + ok = ?FILE_MODULE:delete("file1.txt"), + [] = flush(), ok. run_test(Test, Args) -> - ?line case (catch apply(?MODULE, Test, Args)) of - {'EXIT', _} = Exit -> - {done, Exit, get(test_server_loc)}; - Result -> - {done, Result} - end. + case (catch apply(?MODULE, Test, Args)) of + {'EXIT', _} = Exit -> + {done, Exit, get(test_server_loc)}; + Result -> + {done, Result} + end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -delayed_write(suite) -> - []; -delayed_write(doc) -> - ["Tests the file open option {delayed_write, Size, Delay}"]; +%% Tests the file open option {delayed_write, Size, Delay}. delayed_write(Config) when is_list(Config) -> - Dog = ?t:timetrap(?t:seconds(20)), - - RootDir = ?config(priv_dir, Config), + RootDir = proplists:get_value(priv_dir, Config), File = filename:join(RootDir, - atom_to_list(?MODULE)++"_delayed_write.txt"), + atom_to_list(?MODULE)++"_delayed_write.txt"), Data1 = "asdfghjkl", Data2 = "qwertyuio", Data3 = "zxcvbnm,.", @@ -2934,20 +3031,22 @@ delayed_write(Config) when is_list(Config) -> %% %% Test caching and normal close of non-raw file {ok, Fd1} = - ?FILE_MODULE:open(File, [write, {delayed_write, Size+1, 2000}]), + ?FILE_MODULE:open(File, [write, {delayed_write, Size+1, 400}]), ok = ?FILE_MODULE:write(Fd1, Data1), - ?t:sleep(1000), % Just in case the file system is slow + %% Wait for a reasonable amount of time to check whether the write was + %% practically instantaneous or actually delayed. + timer:sleep(100), {ok, Fd2} = ?FILE_MODULE:open(File, [read]), eof = ?FILE_MODULE:read(Fd2, 1), ok = ?FILE_MODULE:write(Fd1, Data1), % Data flush on size - ?t:sleep(1000), % Just in case the file system is slow + timer:sleep(100), {ok, Data1Data1} = ?FILE_MODULE:pread(Fd2, bof, 2*Size+1), ok = ?FILE_MODULE:write(Fd1, Data1), - ?t:sleep(3000), % Wait until data flush on timeout + timer:sleep(500), % Wait until data flush on timeout {ok, Data1Data1Data1} = ?FILE_MODULE:pread(Fd2, bof, 3*Size+1), ok = ?FILE_MODULE:write(Fd1, Data1), ok = ?FILE_MODULE:close(Fd1), % Data flush on close - ?t:sleep(1000), % Just in case the file system is slow + timer:sleep(100), {ok, Data1Data1Data1Data1} = ?FILE_MODULE:pread(Fd2, bof, 4*Size+1), ok = ?FILE_MODULE:close(Fd2), %% @@ -2955,33 +3054,33 @@ delayed_write(Config) when is_list(Config) -> %% raw file, default parameters. Parent = self(), Fun = fun() -> - Child = self(), - Test = - fun () -> - {ok, Fd} = ?FILE_MODULE:open(File, - [raw, write, delayed_write]), - ok = ?FILE_MODULE:write(Fd, Data1), - Parent ! {Child, wrote}, - receive - {Parent, continue, Reason} -> - {ok, Reason} - end - end, - case (catch Test()) of - {ok, Reason} -> exit(Reason); - Unknown -> - exit({Unknown, get(test_server_loc)}) - end - end, + Child = self(), + Test = + fun () -> + {ok, Fd} = ?FILE_MODULE:open(File, + [raw, write, delayed_write]), + ok = ?FILE_MODULE:write(Fd, Data1), + Parent ! {Child, wrote}, + receive + {Parent, continue, Reason} -> + {ok, Reason} + end + end, + case (catch Test()) of + {ok, Reason} -> exit(Reason); + Unknown -> + exit({Unknown, get(test_server_loc)}) + end + end, Child1 = spawn(Fun), Mref1 = erlang:monitor(process, Child1), receive {Child1, wrote} -> ok; {'DOWN', Mref1, _, _, _} = Down1a -> - ?t:fail(Down1a) + ct:fail(Down1a) end, - ?t:sleep(1000), % Just in case the file system is slow + timer:sleep(100), % Just in case the file system is slow {ok, Fd3} = ?FILE_MODULE:open(File, [read]), eof = ?FILE_MODULE:read(Fd3, 1), Child1 ! {Parent, continue, normal}, @@ -2989,9 +3088,9 @@ delayed_write(Config) when is_list(Config) -> {'DOWN', Mref1, process, Child1, normal} -> ok; {'DOWN', Mref1, _, _, _} = Down1b -> - ?t:fail(Down1b) + ct:fail(Down1b) end, - ?t:sleep(1000), % Just in case the file system is slow + timer:sleep(100), % Just in case the file system is slow {ok, Data1} = ?FILE_MODULE:pread(Fd3, bof, Size+1), ok = ?FILE_MODULE:close(Fd3), %% @@ -3002,9 +3101,9 @@ delayed_write(Config) when is_list(Config) -> {Child2, wrote} -> ok; {'DOWN', Mref2, _, _, _} = Down2a -> - ?t:fail(Down2a) + ct:fail(Down2a) end, - ?t:sleep(1000), % Just in case the file system is slow + timer:sleep(100), % Just in case the file system is slow {ok, Fd4} = ?FILE_MODULE:open(File, [read]), eof = ?FILE_MODULE:read(Fd4, 1), Child2 ! {Parent, continue, kill}, @@ -3012,15 +3111,15 @@ delayed_write(Config) when is_list(Config) -> {'DOWN', Mref2, process, Child2, kill} -> ok; {'DOWN', Mref2, _, _, _} = Down2b -> - ?t:fail(Down2b) + ct:fail(Down2b) end, - ?t:sleep(1000), % Just in case the file system is slow + timer:sleep(100), % Just in case the file system is slow eof = ?FILE_MODULE:pread(Fd4, bof, 1), ok = ?FILE_MODULE:close(Fd4), %% %% Test if file position works with delayed_write {ok, Fd5} = ?FILE_MODULE:open(File, [raw, read, write, - delayed_write]), + delayed_write]), ok = ?FILE_MODULE:truncate(Fd5), ok = ?FILE_MODULE:write(Fd5, [Data1|Data2]), {ok, 0} = ?FILE_MODULE:position(Fd5, bof), @@ -3032,93 +3131,92 @@ delayed_write(Config) when is_list(Config) -> ok = ?FILE_MODULE:close(Fd5), %% [] = flush(), - ?t:timetrap_cancel(Dog), ok. -pid2name(doc) -> "Tests file:pid2name/1."; -pid2name(suite) -> []; +%% Tests file:pid2name/1. pid2name(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(10)), - ?line RootDir = ?config(priv_dir, Config), - ?line Base = test_server:temp_name( - filename:join(RootDir, "pid2name_")), - ?line Name1 = [Base, '.txt'], - ?line Name2 = Base ++ ".txt", - %% - ?line {ok, Pid} = file:open(Name1, [write]), - ?line {ok, Name2} = file:pid2name(Pid), - ?line undefined = file:pid2name(self()), - ?line ok = file:close(Pid), - ?line test_server:sleep(1000), - ?line false = is_process_alive(Pid), - ?line undefined = file:pid2name(Pid), + RootDir = proplists:get_value(priv_dir, Config), + Base = test_server:temp_name( + filename:join(RootDir, "pid2name_")), + Name1 = [Base, '.txt'], + Name2 = Base ++ ".txt", %% - ?line test_server:timetrap_cancel(Dog), + {ok, Pid} = file:open(Name1, [write]), + {ok, Name2} = file:pid2name(Pid), + undefined = file:pid2name(self()), + ok = file:close(Pid), + ct:sleep(1000), + false = is_process_alive(Pid), + undefined = file:pid2name(Pid), ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -read_ahead(suite) -> - []; -read_ahead(doc) -> - ["Tests the file open option {read_ahead, Size}"]; +%% Tests the file open option {read_ahead, Size}. read_ahead(Config) when is_list(Config) -> - ?line Dog = ?t:timetrap(?t:seconds(20)), - %% - ?line RootDir = ?config(priv_dir, Config), - ?line File = filename:join(RootDir, - atom_to_list(?MODULE)++"_read_ahead.txt"), - ?line Data1 = "asdfghjkl", % Must be - ?line Data2 = "qwertyuio", % same - ?line Data3 = "zxcvbnm,.", % length - ?line Size = length(Data1), - ?line Size = length(Data2), - ?line Size = length(Data3), + RootDir = proplists:get_value(priv_dir, Config), + File = filename:join(RootDir, + atom_to_list(?MODULE)++"_read_ahead.txt"), + Data1 = "asdfghjkl", % Must be + Data2 = "qwertyuio", % same + Data3 = "zxcvbnm,.", % length + Size = length(Data1), + Size = length(Data2), + Size = length(Data3), %% %% Test caching of normal non-raw file - ?line {ok, Fd1} = ?FILE_MODULE:open(File, [write]), - ?line ok = ?FILE_MODULE:write(Fd1, [Data1|Data1]), - ?line ?t:sleep(1000), % Just in case the file system is slow - ?line {ok, Fd2} = ?FILE_MODULE:open(File, [read, {read_ahead, 2*Size}]), - ?line {ok, Data1} = ?FILE_MODULE:read(Fd2, Size), - ?line ok = ?FILE_MODULE:pwrite(Fd1, Size, Data2), - ?line ?t:sleep(1000), % Just in case the file system is slow - ?line {ok, Data1} = ?FILE_MODULE:read(Fd2, Size), % Will read cached data - ?line Data2Data2Data2 = Data2++Data2++Data2, - ?line ok = ?FILE_MODULE:pwrite(Fd1, eof, Data2Data2Data2), - ?line ?t:sleep(1000), % Just in case the file system is slow - ?line {ok, Data2Data2Data2} = + {ok, Fd1} = ?FILE_MODULE:open(File, [write]), + ok = ?FILE_MODULE:write(Fd1, [Data1|Data1]), + timer:sleep(1000), % Just in case the file system is slow + {ok, Fd2} = ?FILE_MODULE:open(File, [read, {read_ahead, 2*Size}]), + {ok, Data1} = ?FILE_MODULE:read(Fd2, Size), + ok = ?FILE_MODULE:pwrite(Fd1, Size, Data2), + timer:sleep(1000), % Just in case the file system is slow + {ok, Data1} = ?FILE_MODULE:read(Fd2, Size), % Will read cached data + Data2Data2Data2 = Data2++Data2++Data2, + ok = ?FILE_MODULE:pwrite(Fd1, eof, Data2Data2Data2), + timer:sleep(1000), % Just in case the file system is slow + {ok, Data2Data2Data2} = ?FILE_MODULE:read(Fd2, 3*Size), % Read more than cache buffer - ?line ok = ?FILE_MODULE:close(Fd1), - ?line ok = ?FILE_MODULE:close(Fd2), + ok = ?FILE_MODULE:close(Fd1), + ok = ?FILE_MODULE:close(Fd2), %% Test caching of raw file and default parameters - ?line {ok, Fd3} = ?FILE_MODULE:open(File, [raw, write]), - ?line ok = ?FILE_MODULE:write(Fd3, [Data1|Data1]), - ?line ?t:sleep(1000), % Just in case the file system is slow - ?line {ok, Fd4} = ?FILE_MODULE:open(File, [raw, read, read_ahead]), - ?line {ok, Data1} = ?FILE_MODULE:read(Fd4, Size), - ?line ok = ?FILE_MODULE:pwrite(Fd3, Size, Data2), - ?line ?t:sleep(1000), % Just in case the file system is slow - ?line {ok, Data1} = ?FILE_MODULE:read(Fd4, Size), % Will read cached data - ?line ok = ?FILE_MODULE:close(Fd3), - ?line ok = ?FILE_MODULE:close(Fd4), + {ok, Fd3} = ?FILE_MODULE:open(File, [raw, write]), + ok = ?FILE_MODULE:write(Fd3, [Data1|Data1]), + timer:sleep(1000), % Just in case the file system is slow + {ok, Fd4} = ?FILE_MODULE:open(File, [raw, read, read_ahead]), + {ok, Data1} = ?FILE_MODULE:read(Fd4, Size), + ok = ?FILE_MODULE:pwrite(Fd3, Size, Data2), + timer:sleep(1000), % Just in case the file system is slow + {ok, Data1} = ?FILE_MODULE:read(Fd4, Size), % Will read cached data + ok = ?FILE_MODULE:close(Fd3), + ok = ?FILE_MODULE:close(Fd4), %% Test if the file position works in combination with read_ahead - ?line {ok, Fd5} = ?FILE_MODULE:open(File, [raw, read, write, read_ahead]), - ?line ok = ?FILE_MODULE:truncate(Fd5), - ?line ok = ?FILE_MODULE:write(Fd5, [Data1,Data1|Data3]), - ?line {ok, 0} = ?FILE_MODULE:position(Fd5, bof), - ?line {ok, Data1} = ?FILE_MODULE:read(Fd5, Size), - ?line ok = ?FILE_MODULE:write(Fd5, Data2), - ?line {ok, 0} = ?FILE_MODULE:position(Fd5, bof), - ?line Data1Data2Data3 = Data1++Data2++Data3, - ?line {ok, Data1Data2Data3} = ?FILE_MODULE:read(Fd5, 3*Size+1), - ?line ok = ?FILE_MODULE:close(Fd5), + {ok, Fd5} = ?FILE_MODULE:open(File, [raw, read, write, read_ahead]), + ok = ?FILE_MODULE:truncate(Fd5), + ok = ?FILE_MODULE:write(Fd5, [Data1,Data1|Data3]), + {ok, 0} = ?FILE_MODULE:position(Fd5, bof), + {ok, Data1} = ?FILE_MODULE:read(Fd5, Size), + ok = ?FILE_MODULE:write(Fd5, Data2), + {ok, 0} = ?FILE_MODULE:position(Fd5, bof), + Data1Data2Data3 = Data1++Data2++Data3, + {ok, Data1Data2Data3} = ?FILE_MODULE:read(Fd5, 3*Size+1), + ok = ?FILE_MODULE:close(Fd5), + + %% Ensure that a read that draws from both the buffer and the file won't + %% return anything wonky. + SplitData = << <<(I rem 256)>> || I <- lists:seq(1, 1024) >>, + file:write_file(File, SplitData), + {ok, Fd6} = ?FILE_MODULE:open(File, [raw, read, binary, {read_ahead, 256}]), + {ok, <<1>>} = file:read(Fd6, 1), + <<1, Shifted:512/binary, _Rest/binary>> = SplitData, + {ok, Shifted} = file:read(Fd6, 512), + %% - ?line [] = flush(), - ?line ?t:timetrap_cancel(Dog), + [] = flush(), ok. @@ -3127,137 +3225,131 @@ read_ahead(Config) when is_list(Config) -> -segment_read(suite) -> - []; -segment_read(doc) -> - ["Tests the segmenting of large reads"]; +%% Tests the segmenting of large reads. segment_read(Config) when is_list(Config) -> - ?line Dog = ?t:timetrap(?t:seconds(60)), - %% - ?line Name = filename:join(?config(priv_dir, Config), - ?MODULE_STRING ++ "_segment_read"), - ?line SegSize = 256*1024, - ?line SegCnt = SegSize div 4, - ?line Cnt = 4 * SegCnt, - ?line ok = create_file(Name, Cnt), + Name = filename:join(proplists:get_value(priv_dir, Config), + ?MODULE_STRING ++ "_segment_read"), + SegSize = 256*1024, + SegCnt = SegSize div 4, + Cnt = 4 * SegCnt, + ok = create_file(Name, Cnt), %% %% read_file/1 %% - ?line {ok, Bin} = ?FILE_MODULE:read_file(Name), - ?line true = verify_bin(Bin, 0, Cnt), + {ok, Bin} = ?FILE_MODULE:read_file(Name), + true = verify_bin(Bin, 0, Cnt), %% %% read/2 %% %% Not segmented - ?line {ok, FD1} = ?FILE_MODULE:open(Name, [read, raw, binary]), - ?line {ok, B1a} = ?FILE_MODULE:read(FD1, SegSize), - ?line {ok, B1b} = ?FILE_MODULE:read(FD1, SegSize), - ?line {ok, B1c} = ?FILE_MODULE:read(FD1, SegSize), - ?line {ok, B1d} = ?FILE_MODULE:read(FD1, SegSize), - ?line ok = ?FILE_MODULE:close(FD1), - ?line true = verify_bin(B1a, 0*SegCnt, SegCnt), - ?line true = verify_bin(B1b, 1*SegCnt, SegCnt), - ?line true = verify_bin(B1c, 2*SegCnt, SegCnt), - ?line true = verify_bin(B1d, 3*SegCnt, SegCnt), + {ok, FD1} = ?FILE_MODULE:open(Name, [read, raw, binary]), + {ok, B1a} = ?FILE_MODULE:read(FD1, SegSize), + {ok, B1b} = ?FILE_MODULE:read(FD1, SegSize), + {ok, B1c} = ?FILE_MODULE:read(FD1, SegSize), + {ok, B1d} = ?FILE_MODULE:read(FD1, SegSize), + ok = ?FILE_MODULE:close(FD1), + true = verify_bin(B1a, 0*SegCnt, SegCnt), + true = verify_bin(B1b, 1*SegCnt, SegCnt), + true = verify_bin(B1c, 2*SegCnt, SegCnt), + true = verify_bin(B1d, 3*SegCnt, SegCnt), %% %% Segmented - ?line {ok, FD2} = ?FILE_MODULE:open(Name, [read, raw, binary]), - ?line {ok, B2a} = ?FILE_MODULE:read(FD2, 1*SegSize), - ?line {ok, B2b} = ?FILE_MODULE:read(FD2, 2*SegSize), - ?line {ok, B2c} = ?FILE_MODULE:read(FD2, 2*SegSize), - ?line ok = ?FILE_MODULE:close(FD2), - ?line true = verify_bin(B2a, 0*SegCnt, 1*SegCnt), - ?line true = verify_bin(B2b, 1*SegCnt, 2*SegCnt), - ?line true = verify_bin(B2c, 3*SegCnt, 1*SegCnt), + {ok, FD2} = ?FILE_MODULE:open(Name, [read, raw, binary]), + {ok, B2a} = ?FILE_MODULE:read(FD2, 1*SegSize), + {ok, B2b} = ?FILE_MODULE:read(FD2, 2*SegSize), + {ok, B2c} = ?FILE_MODULE:read(FD2, 2*SegSize), + ok = ?FILE_MODULE:close(FD2), + true = verify_bin(B2a, 0*SegCnt, 1*SegCnt), + true = verify_bin(B2b, 1*SegCnt, 2*SegCnt), + true = verify_bin(B2c, 3*SegCnt, 1*SegCnt), %% %% pread/3 %% - ?line {ok, FD3} = ?FILE_MODULE:open(Name, [read, raw, binary]), + {ok, FD3} = ?FILE_MODULE:open(Name, [read, raw, binary]), %% %% Not segmented - ?line {ok, B3d} = ?FILE_MODULE:pread(FD3, 3*SegSize, SegSize), - ?line {ok, B3c} = ?FILE_MODULE:pread(FD3, 2*SegSize, SegSize), - ?line {ok, B3b} = ?FILE_MODULE:pread(FD3, 1*SegSize, SegSize), - ?line {ok, B3a} = ?FILE_MODULE:pread(FD3, 0*SegSize, SegSize), - ?line true = verify_bin(B3a, 0*SegCnt, SegCnt), - ?line true = verify_bin(B3b, 1*SegCnt, SegCnt), - ?line true = verify_bin(B3c, 2*SegCnt, SegCnt), - ?line true = verify_bin(B3d, 3*SegCnt, SegCnt), + {ok, B3d} = ?FILE_MODULE:pread(FD3, 3*SegSize, SegSize), + {ok, B3c} = ?FILE_MODULE:pread(FD3, 2*SegSize, SegSize), + {ok, B3b} = ?FILE_MODULE:pread(FD3, 1*SegSize, SegSize), + {ok, B3a} = ?FILE_MODULE:pread(FD3, 0*SegSize, SegSize), + true = verify_bin(B3a, 0*SegCnt, SegCnt), + true = verify_bin(B3b, 1*SegCnt, SegCnt), + true = verify_bin(B3c, 2*SegCnt, SegCnt), + true = verify_bin(B3d, 3*SegCnt, SegCnt), %% %% Segmented - ?line {ok, B3g} = ?FILE_MODULE:pread(FD3, 3*SegSize, 2*SegSize), - ?line {ok, B3f} = ?FILE_MODULE:pread(FD3, 1*SegSize, 2*SegSize), - ?line {ok, B3e} = ?FILE_MODULE:pread(FD3, 0*SegSize, 1*SegSize), - ?line true = verify_bin(B3e, 0*SegCnt, 1*SegCnt), - ?line true = verify_bin(B3f, 1*SegCnt, 2*SegCnt), - ?line true = verify_bin(B3g, 3*SegCnt, 1*SegCnt), + {ok, B3g} = ?FILE_MODULE:pread(FD3, 3*SegSize, 2*SegSize), + {ok, B3f} = ?FILE_MODULE:pread(FD3, 1*SegSize, 2*SegSize), + {ok, B3e} = ?FILE_MODULE:pread(FD3, 0*SegSize, 1*SegSize), + true = verify_bin(B3e, 0*SegCnt, 1*SegCnt), + true = verify_bin(B3f, 1*SegCnt, 2*SegCnt), + true = verify_bin(B3g, 3*SegCnt, 1*SegCnt), %% - ?line ok = ?FILE_MODULE:close(FD3), + ok = ?FILE_MODULE:close(FD3), %% %% pread/2 %% - ?line {ok, FD5} = ?FILE_MODULE:open(Name, [read, raw, binary]), + {ok, FD5} = ?FILE_MODULE:open(Name, [read, raw, binary]), %% %% +---+---+---+---+ %% | 4 | 3 | 2 | 1 | %% +---+---+---+---+ %% < ^ > - ?line {ok, [B5d, B5c, B5b, B5a]} = + {ok, [B5d, B5c, B5b, B5a]} = ?FILE_MODULE:pread(FD5, [{3*SegSize, SegSize}, {2*SegSize, SegSize}, {1*SegSize, SegSize}, {0*SegSize, SegSize}]), - ?line true = verify_bin(B5a, 0*SegCnt, SegCnt), - ?line true = verify_bin(B5b, 1*SegCnt, SegCnt), - ?line true = verify_bin(B5c, 2*SegCnt, SegCnt), - ?line true = verify_bin(B5d, 3*SegCnt, SegCnt), + true = verify_bin(B5a, 0*SegCnt, SegCnt), + true = verify_bin(B5b, 1*SegCnt, SegCnt), + true = verify_bin(B5c, 2*SegCnt, SegCnt), + true = verify_bin(B5d, 3*SegCnt, SegCnt), %% %% +---+-------+-------+ %% | 3 | 2 | 1 | %% +---+-------+-------+ %% < ^ ^ > - ?line {ok, [B5g, B5f, B5e]} = + {ok, [B5g, B5f, B5e]} = ?FILE_MODULE:pread(FD5, [{3*SegSize, 2*SegSize}, {1*SegSize, 2*SegSize}, {0*SegSize, 1*SegSize}]), - ?line true = verify_bin(B5e, 0*SegCnt, 1*SegCnt), - ?line true = verify_bin(B5f, 1*SegCnt, 2*SegCnt), - ?line true = verify_bin(B5g, 3*SegCnt, 1*SegCnt), + true = verify_bin(B5e, 0*SegCnt, 1*SegCnt), + true = verify_bin(B5f, 1*SegCnt, 2*SegCnt), + true = verify_bin(B5g, 3*SegCnt, 1*SegCnt), %% %% %% +-------+-----------+ %% | 2 | 1 | %% +-------+-----------+ %% < ^ ^ > - ?line {ok, [B5i, B5h]} = + {ok, [B5i, B5h]} = ?FILE_MODULE:pread(FD5, [{2*SegSize, 3*SegSize}, {0*SegSize, 2*SegSize}]), - ?line true = verify_bin(B5h, 0*SegCnt, 2*SegCnt), - ?line true = verify_bin(B5i, 2*SegCnt, 2*SegCnt), + true = verify_bin(B5h, 0*SegCnt, 2*SegCnt), + true = verify_bin(B5i, 2*SegCnt, 2*SegCnt), %% %% +-------+---+---+ %% | 3 | 2 | 1 | %% +-------+---+---+ %% < ^ ^ > - ?line {ok, [B5l, B5k, B5j]} = + {ok, [B5l, B5k, B5j]} = ?FILE_MODULE:pread(FD5, [{3*SegSize, 1*SegSize}, {2*SegSize, 1*SegSize}, {0*SegSize, 2*SegSize}]), - ?line true = verify_bin(B5j, 0*SegCnt, 2*SegCnt), - ?line true = verify_bin(B5k, 2*SegCnt, 1*SegCnt), - ?line true = verify_bin(B5l, 3*SegCnt, 1*SegCnt), + true = verify_bin(B5j, 0*SegCnt, 2*SegCnt), + true = verify_bin(B5k, 2*SegCnt, 1*SegCnt), + true = verify_bin(B5l, 3*SegCnt, 1*SegCnt), %% %% Real time response time test. %% Req = lists:flatten(lists:duplicate(17, [{2*SegSize, 2*SegSize}, {0*SegSize, 2*SegSize}])), - ?line {{ok, _}, Comment} = + {{ok, _}, Comment} = response_analysis(?FILE_MODULE, pread, [FD5, Req]), - ?line ok = ?FILE_MODULE:close(FD5), + ok = ?FILE_MODULE:close(FD5), %% - ?line [] = flush(), - ?line ?t:timetrap_cancel(Dog), + [] = flush(), {comment, Comment}. @@ -3266,100 +3358,95 @@ segment_read(Config) when is_list(Config) -> -segment_write(suite) -> - []; -segment_write(doc) -> - ["Tests the segmenting of large writes"]; +%% Tests the segmenting of large writes. segment_write(Config) when is_list(Config) -> - ?line Dog = ?t:timetrap(?t:seconds(60)), - %% - ?line Name = filename:join(?config(priv_dir, Config), - ?MODULE_STRING ++ "_segment_write"), - ?line SegSize = 256*1024, - ?line SegCnt = SegSize div 4, - ?line Cnt = 4 * SegCnt, - ?line Bin = create_bin(0, Cnt), + Name = filename:join(proplists:get_value(priv_dir, Config), + ?MODULE_STRING ++ "_segment_write"), + SegSize = 256*1024, + SegCnt = SegSize div 4, + Cnt = 4 * SegCnt, + Bin = create_bin(0, Cnt), %% %% write/2 %% %% Not segmented - ?line {ok, FD1} = ?FILE_MODULE:open(Name, [write, raw, binary]), - ?line ok = ?FILE_MODULE:write(FD1, subbin(Bin, 0*SegSize, 1*SegSize)), - ?line ok = ?FILE_MODULE:write(FD1, subbin(Bin, 1*SegSize, 1*SegSize)), - ?line ok = ?FILE_MODULE:write(FD1, subbin(Bin, 2*SegSize, 1*SegSize)), - ?line ok = ?FILE_MODULE:write(FD1, subbin(Bin, 3*SegSize, 1*SegSize)), - ?line ok = ?FILE_MODULE:close(FD1), - ?line true = verify_file(Name, Cnt), + {ok, FD1} = ?FILE_MODULE:open(Name, [write, raw, binary]), + ok = ?FILE_MODULE:write(FD1, subbin(Bin, 0*SegSize, 1*SegSize)), + ok = ?FILE_MODULE:write(FD1, subbin(Bin, 1*SegSize, 1*SegSize)), + ok = ?FILE_MODULE:write(FD1, subbin(Bin, 2*SegSize, 1*SegSize)), + ok = ?FILE_MODULE:write(FD1, subbin(Bin, 3*SegSize, 1*SegSize)), + ok = ?FILE_MODULE:close(FD1), + true = verify_file(Name, Cnt), %% %% Segmented - ?line {ok, FD2} = ?FILE_MODULE:open(Name, [write, raw, binary]), - ?line ok = ?FILE_MODULE:write(FD2, subbin(Bin, 0*SegSize, 1*SegSize)), - ?line ok = ?FILE_MODULE:write(FD2, subbin(Bin, 1*SegSize, 2*SegSize)), - ?line ok = ?FILE_MODULE:write(FD2, subbin(Bin, 3*SegSize, 1*SegSize)), - ?line ok = ?FILE_MODULE:close(FD2), - ?line true = verify_file(Name, Cnt), + {ok, FD2} = ?FILE_MODULE:open(Name, [write, raw, binary]), + ok = ?FILE_MODULE:write(FD2, subbin(Bin, 0*SegSize, 1*SegSize)), + ok = ?FILE_MODULE:write(FD2, subbin(Bin, 1*SegSize, 2*SegSize)), + ok = ?FILE_MODULE:write(FD2, subbin(Bin, 3*SegSize, 1*SegSize)), + ok = ?FILE_MODULE:close(FD2), + true = verify_file(Name, Cnt), %% %% +---+---+---+---+ %% | | | | | %% +---+---+---+---+ %% < ^ > - ?line ok = write_file(Name, [subbin(Bin, 0*SegSize, 1*SegSize), - subbin(Bin, 1*SegSize, 1*SegSize), - subbin(Bin, 2*SegSize, 1*SegSize), - subbin(Bin, 3*SegSize, 1*SegSize)]), - ?line true = verify_file(Name, Cnt), + ok = write_file(Name, [subbin(Bin, 0*SegSize, 1*SegSize), + subbin(Bin, 1*SegSize, 1*SegSize), + subbin(Bin, 2*SegSize, 1*SegSize), + subbin(Bin, 3*SegSize, 1*SegSize)]), + true = verify_file(Name, Cnt), %% %% +---+-------+---+ %% | | | | %% +---+-------+---+ %% < ^ ^ > - ?line ok = write_file(Name, [subbin(Bin, 0*SegSize, 1*SegSize), - subbin(Bin, 1*SegSize, 2*SegSize), - subbin(Bin, 3*SegSize, 1*SegSize)]), - ?line true = verify_file(Name, Cnt), + ok = write_file(Name, [subbin(Bin, 0*SegSize, 1*SegSize), + subbin(Bin, 1*SegSize, 2*SegSize), + subbin(Bin, 3*SegSize, 1*SegSize)]), + true = verify_file(Name, Cnt), %% %% +-------+-------+ %% | | | %% +-------+-------+ %% < ^ ^ > - ?line ok = write_file(Name, [subbin(Bin, 0*SegSize, 2*SegSize), - subbin(Bin, 2*SegSize, 2*SegSize)]), - ?line true = verify_file(Name, Cnt), + ok = write_file(Name, [subbin(Bin, 0*SegSize, 2*SegSize), + subbin(Bin, 2*SegSize, 2*SegSize)]), + true = verify_file(Name, Cnt), %% %% +-------+---+---+ %% | | | | %% +-------+---+---+ %% < ^ ^ > - ?line ok = write_file(Name, [subbin(Bin, 0*SegSize, 2*SegSize), - subbin(Bin, 2*SegSize, 1*SegSize), - subbin(Bin, 3*SegSize, 1*SegSize)]), - ?line true = verify_file(Name, Cnt), + ok = write_file(Name, [subbin(Bin, 0*SegSize, 2*SegSize), + subbin(Bin, 2*SegSize, 1*SegSize), + subbin(Bin, 3*SegSize, 1*SegSize)]), + true = verify_file(Name, Cnt), %% %% pwrite/3 %% %% Not segmented - ?line {ok, FD3} = ?FILE_MODULE:open(Name, [write, raw, binary]), - ?line ok = ?FILE_MODULE:pwrite(FD3, 3*SegSize, - subbin(Bin, 3*SegSize, 1*SegSize)), - ?line ok = ?FILE_MODULE:pwrite(FD3, 2*SegSize, - subbin(Bin, 2*SegSize, 1*SegSize)), - ?line ok = ?FILE_MODULE:pwrite(FD3, 1*SegSize, - subbin(Bin, 1*SegSize, 1*SegSize)), - ?line ok = ?FILE_MODULE:pwrite(FD3, 0*SegSize, - subbin(Bin, 0*SegSize, 1*SegSize)), - ?line ok = ?FILE_MODULE:close(FD3), - ?line true = verify_file(Name, Cnt), + {ok, FD3} = ?FILE_MODULE:open(Name, [write, raw, binary]), + ok = ?FILE_MODULE:pwrite(FD3, 3*SegSize, + subbin(Bin, 3*SegSize, 1*SegSize)), + ok = ?FILE_MODULE:pwrite(FD3, 2*SegSize, + subbin(Bin, 2*SegSize, 1*SegSize)), + ok = ?FILE_MODULE:pwrite(FD3, 1*SegSize, + subbin(Bin, 1*SegSize, 1*SegSize)), + ok = ?FILE_MODULE:pwrite(FD3, 0*SegSize, + subbin(Bin, 0*SegSize, 1*SegSize)), + ok = ?FILE_MODULE:close(FD3), + true = verify_file(Name, Cnt), %% %% Segmented - ?line {ok, FD4} = ?FILE_MODULE:open(Name, [write, raw, binary]), - ?line ok = ?FILE_MODULE:pwrite(FD4, 3*SegSize, - subbin(Bin, 3*SegSize, 1*SegSize)), - ?line ok = ?FILE_MODULE:pwrite(FD4, 1*SegSize, - subbin(Bin, 1*SegSize, 2*SegSize)), - ?line ok = ?FILE_MODULE:pwrite(FD4, 0*SegSize, - subbin(Bin, 0*SegSize, 1*SegSize)), - ?line ok = ?FILE_MODULE:close(FD4), - ?line true = verify_file(Name, Cnt), + {ok, FD4} = ?FILE_MODULE:open(Name, [write, raw, binary]), + ok = ?FILE_MODULE:pwrite(FD4, 3*SegSize, + subbin(Bin, 3*SegSize, 1*SegSize)), + ok = ?FILE_MODULE:pwrite(FD4, 1*SegSize, + subbin(Bin, 1*SegSize, 2*SegSize)), + ok = ?FILE_MODULE:pwrite(FD4, 0*SegSize, + subbin(Bin, 0*SegSize, 1*SegSize)), + ok = ?FILE_MODULE:close(FD4), + true = verify_file(Name, Cnt), @@ -3367,125 +3454,118 @@ segment_write(Config) when is_list(Config) -> %% pwrite/2 %% %% Not segmented - ?line {ok, FD5} = ?FILE_MODULE:open(Name, [write, raw, binary]), - ?line ok = ?FILE_MODULE:pwrite(FD5, [{3*SegSize, - subbin(Bin, 3*SegSize, 1*SegSize)}]), - ?line ok = ?FILE_MODULE:pwrite(FD5, [{2*SegSize, - subbin(Bin, 2*SegSize, 1*SegSize)}]), - ?line ok = ?FILE_MODULE:pwrite(FD5, [{1*SegSize, - subbin(Bin, 1*SegSize, 1*SegSize)}]), - ?line ok = ?FILE_MODULE:pwrite(FD5, [{0*SegSize, - subbin(Bin, 0*SegSize, 1*SegSize)}]), - ?line ok = ?FILE_MODULE:close(FD5), - ?line true = verify_file(Name, Cnt), + {ok, FD5} = ?FILE_MODULE:open(Name, [write, raw, binary]), + ok = ?FILE_MODULE:pwrite(FD5, [{3*SegSize, + subbin(Bin, 3*SegSize, 1*SegSize)}]), + ok = ?FILE_MODULE:pwrite(FD5, [{2*SegSize, + subbin(Bin, 2*SegSize, 1*SegSize)}]), + ok = ?FILE_MODULE:pwrite(FD5, [{1*SegSize, + subbin(Bin, 1*SegSize, 1*SegSize)}]), + ok = ?FILE_MODULE:pwrite(FD5, [{0*SegSize, + subbin(Bin, 0*SegSize, 1*SegSize)}]), + ok = ?FILE_MODULE:close(FD5), + true = verify_file(Name, Cnt), %% %% Segmented - ?line {ok, FD6} = ?FILE_MODULE:open(Name, [write, raw, binary]), - ?line ok = ?FILE_MODULE:pwrite(FD6, [{3*SegSize, - subbin(Bin, 3*SegSize, 1*SegSize)}]), - ?line ok = ?FILE_MODULE:pwrite(FD6, [{1*SegSize, - subbin(Bin, 1*SegSize, 2*SegSize)}]), - ?line ok = ?FILE_MODULE:pwrite(FD6, [{0*SegSize, - subbin(Bin, 0*SegSize, 1*SegSize)}]), - ?line ok = ?FILE_MODULE:close(FD6), - ?line true = verify_file(Name, Cnt), + {ok, FD6} = ?FILE_MODULE:open(Name, [write, raw, binary]), + ok = ?FILE_MODULE:pwrite(FD6, [{3*SegSize, + subbin(Bin, 3*SegSize, 1*SegSize)}]), + ok = ?FILE_MODULE:pwrite(FD6, [{1*SegSize, + subbin(Bin, 1*SegSize, 2*SegSize)}]), + ok = ?FILE_MODULE:pwrite(FD6, [{0*SegSize, + subbin(Bin, 0*SegSize, 1*SegSize)}]), + ok = ?FILE_MODULE:close(FD6), + true = verify_file(Name, Cnt), %% %% +---+---+---+---+ %% | 4 | 3 | 2 | 1 | %% +---+---+---+---+ %% < ^ > - ?line ok = pwrite_file(Name, [{3*SegSize, - subbin(Bin, 3*SegSize, 1*SegSize)}, - {2*SegSize, - subbin(Bin, 2*SegSize, 1*SegSize)}, - {1*SegSize, - subbin(Bin, 1*SegSize, 1*SegSize)}, - {0*SegSize, - subbin(Bin, 0*SegSize, 1*SegSize)}]), - ?line true = verify_file(Name, Cnt), + ok = pwrite_file(Name, [{3*SegSize, + subbin(Bin, 3*SegSize, 1*SegSize)}, + {2*SegSize, + subbin(Bin, 2*SegSize, 1*SegSize)}, + {1*SegSize, + subbin(Bin, 1*SegSize, 1*SegSize)}, + {0*SegSize, + subbin(Bin, 0*SegSize, 1*SegSize)}]), + true = verify_file(Name, Cnt), %% %% +---+-------+---+ %% | 3 | 2 | 1 | %% +---+-------+---+ %% < ^ ^ > - ?line ok = pwrite_file(Name, [{3*SegSize, - subbin(Bin, 3*SegSize, 1*SegSize)}, - {1*SegSize, - subbin(Bin, 1*SegSize, 2*SegSize)}, - {0*SegSize, - subbin(Bin, 0*SegSize, 1*SegSize)}]), - ?line true = verify_file(Name, Cnt), + ok = pwrite_file(Name, [{3*SegSize, + subbin(Bin, 3*SegSize, 1*SegSize)}, + {1*SegSize, + subbin(Bin, 1*SegSize, 2*SegSize)}, + {0*SegSize, + subbin(Bin, 0*SegSize, 1*SegSize)}]), + true = verify_file(Name, Cnt), %% %% +-------+-------+ %% | 2 | 1 | %% +-------+-------+ %% < ^ ^ > - ?line ok = pwrite_file(Name, [{2*SegSize, - subbin(Bin, 2*SegSize, 2*SegSize)}, - {0*SegSize, - subbin(Bin, 0*SegSize, 2*SegSize)}]), - ?line true = verify_file(Name, Cnt), + ok = pwrite_file(Name, [{2*SegSize, + subbin(Bin, 2*SegSize, 2*SegSize)}, + {0*SegSize, + subbin(Bin, 0*SegSize, 2*SegSize)}]), + true = verify_file(Name, Cnt), %% %% +-------+---+---+ %% | 3 | 2 | 1 | %% +-------+---+---+ %% < ^ ^ > - ?line ok = pwrite_file(Name, [{3*SegSize, - subbin(Bin, 3*SegSize, 1*SegSize)}, - {2*SegSize, - subbin(Bin, 2*SegSize, 1*SegSize)}, - {0*SegSize, - subbin(Bin, 0*SegSize, 2*SegSize)}]), - ?line true = verify_file(Name, Cnt), + ok = pwrite_file(Name, [{3*SegSize, + subbin(Bin, 3*SegSize, 1*SegSize)}, + {2*SegSize, + subbin(Bin, 2*SegSize, 1*SegSize)}, + {0*SegSize, + subbin(Bin, 0*SegSize, 2*SegSize)}]), + true = verify_file(Name, Cnt), %% %% Real time response time test. %% - ?line {ok, FD7} = ?FILE_MODULE:open(Name, [write, raw, binary]), + {ok, FD7} = ?FILE_MODULE:open(Name, [write, raw, binary]), Req = lists:flatten(lists:duplicate(17, [{2*SegSize, subbin(Bin, 2*SegSize, 2*SegSize)}, - {0*SegSize, - subbin(Bin, 0*SegSize, 2*SegSize)}])), - ?line {ok, Comment} = + {0*SegSize, + subbin(Bin, 0*SegSize, 2*SegSize)}])), + {ok, Comment} = response_analysis(?FILE_MODULE, pwrite, [FD7, Req]), - ?line ok = ?FILE_MODULE:close(FD7), + ok = ?FILE_MODULE:close(FD7), %% - ?line [] = flush(), - ?line ?t:timetrap_cancel(Dog), + [] = flush(), {comment, Comment}. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -ipread(suite) -> - []; -ipread(doc) -> - ["Test Dets special indirect pread"]; +%% Test Dets special indirect pread. ipread(Config) when is_list(Config) -> - ?line Dog = ?t:timetrap(?t:seconds(30)), - %% - ?line Dir = ?config(priv_dir, Config), - ?line ok = ipread_int(Dir, [raw, binary]), - ?line ok = ipread_int(Dir, [raw]), - ?line ok = ipread_int(Dir, [binary]), - ?line ok = ipread_int(Dir, []), - ?line ok = ipread_int(Dir, [ram, binary]), - ?line ok = ipread_int(Dir, [ram]), + Dir = proplists:get_value(priv_dir, Config), + ok = ipread_int(Dir, [raw, binary]), + ok = ipread_int(Dir, [raw]), + ok = ipread_int(Dir, [binary]), + ok = ipread_int(Dir, []), + ok = ipread_int(Dir, [ram, binary]), + ok = ipread_int(Dir, [ram]), %% - ?line [] = flush(), - ?line ?t:timetrap_cancel(Dog), + [] = flush(), ok. ipread_int(Dir, ModeList) -> - ?line Name = + Name = filename:join(Dir, lists:flatten([?MODULE_STRING, "_ipread", - lists:map(fun (X) -> - ["_", atom_to_list(X)] - end, - ModeList)])), - ?line io:format("ipread_int<~p, ~p>~n", [Name, ModeList]), - ?line {Conv, Sizeof} = + lists:map(fun (X) -> + ["_", atom_to_list(X)] + end, + ModeList)])), + io:format("ipread_int<~p, ~p>~n", [Name, ModeList]), + {Conv, Sizeof} = case lists:member(binary, ModeList) of true -> {fun (Bin) when is_binary(Bin) -> Bin; @@ -3498,144 +3578,130 @@ ipread_int(Dir, ModeList) -> end, fun erlang:length/1} end, - ?line Pos = 4711, - ?line Data = Conv("THE QUICK BROWN FOX JUMPS OVER A LAZY DOG"), - ?line Size = Sizeof(Data), - ?line Init = Conv(" "), - ?line SizeInit = Sizeof(Init), - ?line Head = Conv(<<Size:32/big-unsigned, Pos:32/big-unsigned>>), - ?line Filler = Conv(bytes($ , Pos-SizeInit-Sizeof(Head))), - ?line Size1 = Size+1, - ?line SizePos = Size+Pos, + Pos = 4711, + Data = Conv("THE QUICK BROWN FOX JUMPS OVER A LAZY DOG"), + Size = Sizeof(Data), + Init = Conv(" "), + SizeInit = Sizeof(Init), + Head = Conv(<<Size:32/big-unsigned, Pos:32/big-unsigned>>), + Filler = Conv(bytes($ , Pos-SizeInit-Sizeof(Head))), + Size1 = Size+1, + SizePos = Size+Pos, %% - ?line {ok, FD} = ?FILE_MODULE:open(Name, [write, read | ModeList]), - ?line ok = ?FILE_MODULE:truncate(FD), - ?line ok = ?FILE_MODULE:write(FD, Init), - ?line ok = ?FILE_MODULE:write(FD, Head), - ?line ok = ?FILE_MODULE:write(FD, Filler), - ?line ok = ?FILE_MODULE:write(FD, Data), + {ok, FD} = ?FILE_MODULE:open(Name, [write, read | ModeList]), + ok = ?FILE_MODULE:truncate(FD), + ok = ?FILE_MODULE:write(FD, Init), + ok = ?FILE_MODULE:write(FD, Head), + ok = ?FILE_MODULE:write(FD, Filler), + ok = ?FILE_MODULE:write(FD, Data), %% Correct read - ?line {ok, {Size, Pos, Data}} = + {ok, {Size, Pos, Data}} = ?FILE_MODULE:ipread_s32bu_p32bu(FD, SizeInit, infinity), %% Invalid header - size > max - ?line eof = + eof = ?FILE_MODULE:ipread_s32bu_p32bu(FD, SizeInit, Size-1), %% Data block protudes over eof - ?line ok = + ok = ?FILE_MODULE:pwrite(FD, SizeInit, <<Size1:32/big-unsigned, - Pos:32/big-unsigned>>), - ?line {ok, {Size1, Pos, Data}} = + Pos:32/big-unsigned>>), + {ok, {Size1, Pos, Data}} = ?FILE_MODULE:ipread_s32bu_p32bu(FD, SizeInit, Size1), %% Data block outside file - ?line ok = + ok = ?FILE_MODULE:pwrite(FD, SizeInit, <<Size:32/big-unsigned, - SizePos:32/big-unsigned>>), - ?line {ok, {Size, SizePos, eof}} = + SizePos:32/big-unsigned>>), + {ok, {Size, SizePos, eof}} = ?FILE_MODULE:ipread_s32bu_p32bu(FD, SizeInit, Size), %% Zero size - ?line ok = + ok = ?FILE_MODULE:pwrite(FD, SizeInit, <<0:32/big-unsigned, - Pos:32/big-unsigned>>), - ?line {ok, {0, Pos, eof}} = + Pos:32/big-unsigned>>), + {ok, {0, Pos, eof}} = ?FILE_MODULE:ipread_s32bu_p32bu(FD, SizeInit, Size), %% Invalid header - protudes over eof - ?line eof = + eof = ?FILE_MODULE:ipread_s32bu_p32bu(FD, Pos+Size-(Sizeof(Head)-1), infinity), %% Header not even in file - ?line eof = + eof = ?FILE_MODULE:ipread_s32bu_p32bu(FD, Pos+Size, infinity), %% - ?line ok = ?FILE_MODULE:close(FD), + ok = ?FILE_MODULE:close(FD), ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -interleaved_read_write(suite) -> - []; -interleaved_read_write(doc) -> - ["Tests interleaved read and writes"]; +%% Tests interleaved read and writes. interleaved_read_write(Config) when is_list(Config) -> - ?line Dog = ?t:timetrap(?t:seconds(30)), - %% - ?line Dir = ?config(priv_dir, Config), - ?line File = + Dir = proplists:get_value(priv_dir, Config), + File = filename:join(Dir, ?MODULE_STRING++"interleaved_read_write.txt"), - ?line {ok,F1} = ?FILE_MODULE:open(File, [write]), - ?line ok = ?FILE_MODULE:write(F1, "data---r1."), % 10 chars each - ?line ok = ?FILE_MODULE:write(F1, "data---r2."), - ?line ok = ?FILE_MODULE:write(F1, "data---r3."), - ?line ok = ?FILE_MODULE:close(F1), - ?line {ok,F2} = ?FILE_MODULE:open(File, [read, write]), - ?line {ok, "data---r1."} = ?FILE_MODULE:read(F2, 10), - ?line ok = ?FILE_MODULE:write(F2, "data---w2."), - ?line ok = ?FILE_MODULE:close(F2), - ?line {ok,F3} = ?FILE_MODULE:open(File, [read]), - ?line {ok, "data---r1."} = ?FILE_MODULE:read(F3, 10), - ?line {ok, "data---w2."} = ?FILE_MODULE:read(F3, 10), - ?line {ok, "data---r3."} = ?FILE_MODULE:read(F3, 10), - ?line eof = ?FILE_MODULE:read(F3, 1), - ?line ok = ?FILE_MODULE:close(F2), + {ok,F1} = ?FILE_MODULE:open(File, [write]), + ok = ?FILE_MODULE:write(F1, "data---r1."), % 10 chars each + ok = ?FILE_MODULE:write(F1, "data---r2."), + ok = ?FILE_MODULE:write(F1, "data---r3."), + ok = ?FILE_MODULE:close(F1), + {ok,F2} = ?FILE_MODULE:open(File, [read, write]), + {ok, "data---r1."} = ?FILE_MODULE:read(F2, 10), + ok = ?FILE_MODULE:write(F2, "data---w2."), + ok = ?FILE_MODULE:close(F2), + {ok,F3} = ?FILE_MODULE:open(File, [read]), + {ok, "data---r1."} = ?FILE_MODULE:read(F3, 10), + {ok, "data---w2."} = ?FILE_MODULE:read(F3, 10), + {ok, "data---r3."} = ?FILE_MODULE:read(F3, 10), + eof = ?FILE_MODULE:read(F3, 1), + ok = ?FILE_MODULE:close(F2), %% - ?line [] = flush(), - ?line ?t:timetrap_cancel(Dog), + [] = flush(), ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -otp_5814(suite) -> - []; -otp_5814(doc) -> - ["OTP-5814. eval/consult/script return correct line numbers"]; +%% OTP-5814. eval/consult/script return correct line numbers. otp_5814(Config) when is_list(Config) -> - ?line Dog = ?t:timetrap(?t:seconds(10)), - PrivDir = ?config(priv_dir, Config), + PrivDir = proplists:get_value(priv_dir, Config), File = filename:join(PrivDir, "otp_5814"), Path = [PrivDir], - ?line ok = file:write_file(File, <<"{a,b,c}. + ok = file:write_file(File, <<"{a,b,c}. a. - b. - c. - {d,e, - [}.">>), - ?line {error, {6,erl_parse,_}} = file:eval(File), - ?line {error, {6,erl_parse,_}} = file:consult(File), - ?line {error, {6,erl_parse,_}} = file:path_consult(Path, File), - ?line {error, {6,erl_parse,_}} = file:path_eval(Path, File), - ?line {error, {6,erl_parse,_}} = file:script(File), - ?line {error, {6,erl_parse,_}} = file:path_script(Path, File), - - ?line ok = file:write_file(File, <<>>), - ?line {error, {1,file,undefined_script}} = file:path_script(Path, File), +b. +c. +{d,e, + [}.">>), + {error, {6,erl_parse,_}} = file:eval(File), + {error, {6,erl_parse,_}} = file:consult(File), + {error, {6,erl_parse,_}} = file:path_consult(Path, File), + {error, {6,erl_parse,_}} = file:path_eval(Path, File), + {error, {6,erl_parse,_}} = file:script(File), + {error, {6,erl_parse,_}} = file:path_script(Path, File), + + ok = file:write_file(File, <<>>), + {error, {1,file,undefined_script}} = file:path_script(Path, File), %% The error is not propagated... - ?line ok = file:write_file(File, <<"a. + ok = file:write_file(File, <<"a. b. - 1/0.">>), - ?line {error, {3, file, {error, badarith, _}}} = file:eval(File), - - ?line ok = file:write_file(File, <<"erlang:raise(throw, apa, []).">>), - ?line {error, {1, file, {throw, apa, _}}} = file:eval(File), +1/0.">>), + {error, {3, file, {error, badarith, _}}} = file:eval(File), - file:delete(File), - ?line ?t:timetrap_cancel(Dog), - ok. +ok = file:write_file(File, <<"erlang:raise(throw, apa, []).">>), +{error, {1, file, {throw, apa, _}}} = file:eval(File), + +file:delete(File), +ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -otp_10852(suite) -> - []; -otp_10852(doc) -> - ["OTP-10852. +fnu and latin1 filenames"]; +%% OTP-10852. +fnu and latin1 filenames. otp_10852(Config) when is_list(Config) -> Node = start_node(erl_pp_helper, "+fnu"), - Dir = ?config(priv_dir, Config), + Dir = proplists:get_value(priv_dir, Config), B = filename:join(Dir, <<"\xE4">>), ok = rpc_call(Node, get_cwd, [B]), {error, no_translation} = rpc_call(Node, set_cwd, [B]), @@ -3650,10 +3716,10 @@ otp_10852(Config) when is_list(Config) -> ok = rpc_call(Node, read_file, [B]), ok = rpc_call(Node, make_link, [B,B]), case rpc_call(Node, make_symlink, [B,B]) of - ok -> ok; - {error, E} when (E =:= enotsup) or (E =:= eperm) -> - {win32,_} = os:type() - end, + ok -> ok; + {error, E} when (E =:= enotsup) or (E =:= eperm) -> + {win32,_} = os:type() + end, ok = rpc_call(Node, delete, [B]), ok = rpc_call(Node, make_dir, [B]), ok = rpc_call(Node, del_dir, [B]), @@ -3676,58 +3742,58 @@ rpc_call(N, F, As) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -large_file(suite) -> - []; -large_file(doc) -> - ["Tests positioning in large files (> 4G)"]; +large_file() -> + [{timetrap,{minutes,20}}]. + +%% Tests positioning in large files (> 4G). large_file(Config) when is_list(Config) -> run_large_file_test(Config, fun(Name) -> do_large_file(Name) end, "_large_file"). do_large_file(Name) -> - ?line Watchdog = ?t:timetrap(?t:minutes(20)), - - ?line S = "1234567890", + S = "1234567890", L = length(S), R = lists:reverse(S), P = 1 bsl 32, Ss = lists:sort(S), Rs = lists:reverse(Ss), - ?line {ok,F} = ?FILE_MODULE:open(Name, [raw,read,write]), - ?line ok = ?FILE_MODULE:write(F, S), - ?line {ok,P} = ?FILE_MODULE:position(F, P), - ?line ok = ?FILE_MODULE:write(F, R), - ?line {ok,0} = ?FILE_MODULE:position(F, bof), - ?line {ok,S} = ?FILE_MODULE:read(F, L), - ?line {ok,P} = ?FILE_MODULE:position(F, {eof,-L}), - ?line {ok,R} = ?FILE_MODULE:read(F, L+1), - ?line {ok,S} = ?FILE_MODULE:pread(F, 0, L), - ?line {ok,R} = ?FILE_MODULE:pread(F, P, L+1), - ?line ok = ?FILE_MODULE:pwrite(F, 0, Ss), - ?line ok = ?FILE_MODULE:pwrite(F, P, Rs), - ?line {ok,0} = ?FILE_MODULE:position(F, bof), - ?line {ok,Ss} = ?FILE_MODULE:read(F, L), - ?line {ok,P} = ?FILE_MODULE:position(F, {eof,-L}), - ?line {ok,Rs} = ?FILE_MODULE:read(F, L+1), - ?line ok = ?FILE_MODULE:close(F), + {ok,F} = ?FILE_MODULE:open(Name, [raw,read,write]), + ok = ?FILE_MODULE:write(F, S), + {ok,P} = ?FILE_MODULE:position(F, P), + ok = ?FILE_MODULE:write(F, R), + {ok,0} = ?FILE_MODULE:position(F, bof), + {ok,S} = ?FILE_MODULE:read(F, L), + {ok,P} = ?FILE_MODULE:position(F, {eof,-L}), + {ok,R} = ?FILE_MODULE:read(F, L+1), + {ok,S} = ?FILE_MODULE:pread(F, 0, L), + {ok,R} = ?FILE_MODULE:pread(F, P, L+1), + ok = ?FILE_MODULE:pwrite(F, 0, Ss), + ok = ?FILE_MODULE:pwrite(F, P, Rs), + {ok,0} = ?FILE_MODULE:position(F, bof), + {ok,Ss} = ?FILE_MODULE:read(F, L), + {ok,P} = ?FILE_MODULE:position(F, {eof,-L}), + {ok,Rs} = ?FILE_MODULE:read(F, L+1), + ok = ?FILE_MODULE:close(F), %% Reopen the file with 'append'; used to fail on Windows causing %% writes to go to the beginning of the file for files > 4GB. - ?line PL = P + L, - ?line PLL = PL + L, - ?line {ok,F1} = ?FILE_MODULE:open(Name, [raw,read,write,append]), - ?line ok = ?FILE_MODULE:write(F1, R), - ?line {ok,PLL} = ?FILE_MODULE:position(F1, {cur,0}), - ?line {ok,Rs} = ?FILE_MODULE:pread(F1, P, L), - ?line {ok,PL} = ?FILE_MODULE:position(F1, {eof,-L}), - ?line {ok,R} = ?FILE_MODULE:read(F1, L+1), - ?line ok = ?FILE_MODULE:close(F1), - %% - ?line ?t:timetrap_cancel(Watchdog), + PL = P + L, + PLL = PL + L, + {ok,F1} = ?FILE_MODULE:open(Name, [raw,read,write,append]), + ok = ?FILE_MODULE:write(F1, R), + {ok,PLL} = ?FILE_MODULE:position(F1, {cur,0}), + {ok,Rs} = ?FILE_MODULE:pread(F1, P, L), + {ok,PL} = ?FILE_MODULE:position(F1, {eof,-L}), + {ok,R} = ?FILE_MODULE:read(F1, L+1), + ok = ?FILE_MODULE:close(F1), + ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +large_write() -> + [{timetrap,{minutes,20}}]. + large_write(Config) when is_list(Config) -> run_large_file_test(Config, fun(Name) -> do_large_write(Name) end, @@ -3753,18 +3819,95 @@ do_large_write(Name) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Benchmarks +%% +%% Note that we only measure the time it takes to run the isolated file +%% operations and that the actual test runtime can differ significantly, +%% especially on the write side as the files need to be truncated before +%% writing. + +large_writes(Config) when is_list(Config) -> + Modes = [raw, binary], + OpCount = 4096, + Data = <<0:(64 bsl 10)/unit:8>>, + run_write_benchmark(Config, Modes, OpCount, Data). + +large_writes_delayed(Config) when is_list(Config) -> + %% Each write is exactly as large as the delay buffer, causing the writes + %% to pass through each time, giving us a decent idea of how much overhead + %% delayed_write adds. + Modes = [raw, binary, {delayed_write, 64 bsl 10, 2000}], + OpCount = 4096, + Data = <<0:(64 bsl 10)/unit:8>>, + run_write_benchmark(Config, Modes, OpCount, Data). + +tiny_writes(Config) when is_list(Config) -> + Modes = [raw, binary], + OpCount = 512 bsl 10, + Data = <<0>>, + run_write_benchmark(Config, Modes, OpCount, Data). + +tiny_writes_delayed(Config) when is_list(Config) -> + Modes = [raw, binary, {delayed_write, 512 bsl 10, 2000}], + OpCount = 512 bsl 10, + Data = <<0>>, + run_write_benchmark(Config, Modes, OpCount, Data). + +%% The read benchmarks assume that "benchmark_scratch_file" has been filled by +%% the write benchmarks. + +tiny_reads(Config) when is_list(Config) -> + Modes = [raw, binary], + OpCount = 512 bsl 10, + run_read_benchmark(Config, Modes, OpCount, 1). + +tiny_reads_ahead(Config) when is_list(Config) -> + Modes = [raw, binary, {read_ahead, 512 bsl 10}], + OpCount = 512 bsl 10, + run_read_benchmark(Config, Modes, OpCount, 1). + +run_write_benchmark(Config, Modes, OpCount, Data) -> + run_benchmark(Config, [write | Modes], OpCount, fun file:write/2, Data). + +run_read_benchmark(Config, Modes, OpCount, OpSize) -> + run_benchmark(Config, [read | Modes], OpCount, fun file:read/2, OpSize). + +run_benchmark(Config, Modes, OpCount, Fun, Arg) -> + ScratchDir = proplists:get_value(priv_dir, Config), + Path = filename:join(ScratchDir, "benchmark_scratch_file"), + {ok, Fd} = file:open(Path, Modes), + submit_throughput_results(Fun, [Fd, Arg], OpCount). + +submit_throughput_results(Fun, Args, Times) -> + MSecs = measure_repeated_file_op(Fun, Args, Times, millisecond), + IOPS = trunc(Times * (1000 / MSecs)), + ct_event:notify(#event{ name = benchmark_data, data = [{value,IOPS}] }), + {comment, io_lib:format("~p IOPS, ~p ms", [IOPS, trunc(MSecs)])}. + +measure_repeated_file_op(Fun, Args, Times, Unit) -> + Start = os:perf_counter(Unit), + repeated_apply(Fun, Args, Times), + os:perf_counter(Unit) - Start. + +repeated_apply(_F, _Args, Times) when Times =< 0 -> + ok; +repeated_apply(F, Args, Times) -> + erlang:apply(F, Args), + repeated_apply(F, Args, Times - 1). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% response_analysis(Module, Function, Arguments) -> Parent = self(), - ?line erlang:yield(), % Schedule out before test - ?line Child = + erlang:yield(), % Schedule out before test + Child = spawn_link( fun () -> receive {Parent, start, Ts} -> ok end, Stat = iterate(response_stat(response_stat(init, Ts), - erlang:now()), + micro_ts()), done, fun (S) -> erlang:yield(), @@ -3772,31 +3915,32 @@ response_analysis(Module, Function, Arguments) -> {Parent, stop} -> done after 0 -> - response_stat(S, erlang:now()) + response_stat(S, micro_ts()) end end), - Parent ! {self(), stopped, response_stat(Stat, erlang:now())} + Parent ! {self(), stopped, response_stat(Stat, micro_ts())} end), - ?line Child ! {Parent, start, erlang:now()}, - ?line Result = apply(Module, Function, Arguments), - ?line Child ! {Parent, stop}, - ?line {N, Sum, _, M, Max} = receive {Child, stopped, X} -> X end, - ?line Mean_ms = (0.001*Sum) / (N-1), - ?line Max_ms = 0.001 * Max, - ?line Comment = + Child ! {Parent, start, micro_ts()}, + Result = apply(Module, Function, Arguments), + Child ! {Parent, stop}, + {N, Sum, _, M, Max} = receive {Child, stopped, X} -> X end, + Mean_ms = (0.001*Sum) / (N-1), + Max_ms = 0.001 * Max, + Comment = lists:flatten( io_lib:format( "Scheduling interval: Mean = ~.3f ms, " ++"Max = ~.3f ms for no ~p of ~p.~n", [Mean_ms, Max_ms, M, (N-1)])), - ?line {Result, Comment}. - + {Result, Comment}. +micro_ts() -> + erlang:monotonic_time(microsecond). response_stat(init, Ts) -> {0, 0, Ts, 0, 0}; -response_stat({N, Sum, {A1, B1, C1}, M, Max}, {A2, B2, C2} = Ts) -> - D = C2-C1 + 1000000*((B2-B1) + 1000000*(A2-A1)), +response_stat({N, Sum, Ts0, M, Max}, Ts) -> + D = Ts - Ts0, if D > Max -> {N+1, Sum+D, Ts, N, D}; true -> @@ -3813,10 +3957,10 @@ response_stat({N, Sum, {A1, B1, C1}, M, Max}, {A2, B2, C2} = Ts) -> %% create_file/2 below is some 44 times faster. create_file_slow(Name, N) when is_integer(N), N >= 0 -> - ?line {ok, FD} = + {ok, FD} = ?FILE_MODULE:open(Name, [raw, write, delayed_write, binary]), - ?line ok = create_file_slow(FD, 0, N), - ?line ok = ?FILE_MODULE:close(FD), + ok = create_file_slow(FD, 0, N), + ok = ?FILE_MODULE:close(FD), ok. create_file_slow(_FD, M, M) -> @@ -3831,10 +3975,10 @@ create_file_slow(FD, M, N) -> %% from 0 to N-1. create_file(Name, N) when is_integer(N), N >= 0 -> - ?line {ok, FD} = + {ok, FD} = ?FILE_MODULE:open(Name, [raw, write, delayed_write, binary]), - ?line ok = create_file(FD, 0, N), - ?line ok = ?FILE_MODULE:close(FD), + ok = create_file(FD, 0, N), + ok = ?FILE_MODULE:close(FD), ok. create_file(_FD, M, M) -> @@ -3851,10 +3995,10 @@ create_file(FD, M, N0, R) when M + 8 =< N0 -> N1 = N0-1, N2 = N0-2, N3 = N0-3, N4 = N0-4, N5 = N0-5, N6 = N0-6, N7 = N0-7, N8 = N0-8, create_file(FD, M, N8, - [<<N8:32/unsigned, N7:32/unsigned, - N6:32/unsigned, N5:32/unsigned, - N4:32/unsigned, N3:32/unsigned, - N2:32/unsigned, N1:32/unsigned>> | R]); + [<<N8:32/unsigned, N7:32/unsigned, + N6:32/unsigned, N5:32/unsigned, + N4:32/unsigned, N3:32/unsigned, + N2:32/unsigned, N1:32/unsigned>> | R]); create_file(FD, M, N0, R) -> N1 = N0-1, create_file(FD, M, N1, [<<N1:32/unsigned>> | R]). @@ -3871,14 +4015,14 @@ create_bin(M, N0, R) when M+8 =< N0 -> N5 = N0-5, N6 = N0-6, N7 = N0-7, N8 = N0-8, create_bin(M, N8, [<<N8:32/unsigned, N7:32/unsigned, - N6:32/unsigned, N5:32/unsigned, - N4:32/unsigned, N3:32/unsigned, - N2:32/unsigned, N1:32/unsigned>> | R]); + N6:32/unsigned, N5:32/unsigned, + N4:32/unsigned, N3:32/unsigned, + N2:32/unsigned, N1:32/unsigned>> | R]); create_bin(M, N0, R) -> N1 = N0-1, create_bin(M, N1, [<<N1:32/unsigned>> | R]). - - + + verify_bin(<<>>, _, 0) -> @@ -3890,8 +4034,8 @@ verify_bin(Bin, N, Cnt) -> N4 = N + 4, N5 = N + 5, N6 = N + 6, N7 = N + 7, case Bin of <<N0:32/unsigned, N1:32/unsigned, N2:32/unsigned, N3:32/unsigned, - N4:32/unsigned, N5:32/unsigned, N6:32/unsigned, N7:32/unsigned, - B/binary>> -> + N4:32/unsigned, N5:32/unsigned, N6:32/unsigned, N7:32/unsigned, + B/binary>> -> verify_bin(B, N+8, Cnt-8); <<N:32/unsigned, B/binary>> -> verify_bin(B, N+1, Cnt-1); @@ -3972,13 +4116,13 @@ pwrite_file(Name, Data) -> read_line_testdata(PrivDir) -> All0 = [{fun read_line_create0/1,"Testdata1.txt",5,10}, - {fun read_line_create1/1,"Testdata2.txt",401,802}, - {fun read_line_create2/1,"Testdata3.txt",1,2}, - {fun read_line_create3/1,"Testdata4.txt",601,fail}, - {fun read_line_create4/1,"Testdata5.txt",601,1002}, - {fun read_line_create5/1,"Testdata6.txt",601,1202}, - {fun read_line_create6/1,"Testdata7.txt",601,1202}, - {fun read_line_create7/1,"Testdata8.txt",4001,8002}], + {fun read_line_create1/1,"Testdata2.txt",401,802}, + {fun read_line_create2/1,"Testdata3.txt",1,2}, + {fun read_line_create3/1,"Testdata4.txt",601,fail}, + {fun read_line_create4/1,"Testdata5.txt",601,1002}, + {fun read_line_create5/1,"Testdata6.txt",601,1202}, + {fun read_line_create6/1,"Testdata7.txt",601,1202}, + {fun read_line_create7/1,"Testdata8.txt",4001,8002}], [ {A,filename:join([PrivDir,B]),C,D} || {A,B,C,D} <- All0 ]. read_line_create_files(TestData) -> @@ -3987,105 +4131,93 @@ read_line_create_files(TestData) -> read_line_remove_files(TestData) -> [ file:delete(File) || {_Function,File,_,_} <- TestData ]. -read_line_1(suite) -> - []; -read_line_1(doc) -> - ["read_line with prim_file"]; +%% read_line with ?PRIM_FILE. read_line_1(Config) when is_list(Config) -> - ?line PrivDir = ?config(priv_dir, Config), - ?line All = read_line_testdata(PrivDir), - ?line read_line_create_files(All), - ?line [ begin - io:format("read_line_all: ~s~n",[File]), - {X,_} = read_line_all(File), - true - end || {_,File,X,_} <- All ], - ?line [ begin - io:format("read_line_all_alternating: ~s~n",[File]), - {Y,_} = read_line_all_alternating(File), - true - end || {_,File,_,Y} <- All , Y =/= fail], - ?line [ begin - io:format("read_line_all_alternating (failing as should): ~s~n",[File]), - {'EXIT',_} = (catch read_line_all_alternating(File)), - true - end || {_,File,_,Y} <- All , Y =:= fail], - ?line read_line_remove_files(All), + PrivDir = proplists:get_value(priv_dir, Config), + All = read_line_testdata(PrivDir), + read_line_create_files(All), + [ begin + io:format("read_line_all: ~s~n",[File]), + {X,_} = read_line_all(File), + true + end || {_,File,X,_} <- All ], + [ begin + io:format("read_line_all_alternating: ~s~n",[File]), + {Y,_} = read_line_all_alternating(File), + true + end || {_,File,_,Y} <- All , Y =/= fail], + [ begin + io:format("read_line_all_alternating (failing as should): ~s~n",[File]), + {'EXIT',_} = (catch read_line_all_alternating(File)), + true + end || {_,File,_,Y} <- All , Y =:= fail], + read_line_remove_files(All), ok. -read_line_2(suite) -> - []; -read_line_2(doc) -> - ["read_line with file"]; +%% read_line with file. read_line_2(Config) when is_list(Config) -> - ?line PrivDir = ?config(priv_dir, Config), - ?line All = read_line_testdata(PrivDir), - ?line read_line_create_files(All), - ?line [ begin - io:format("read_line_all: ~s~n",[File]), - {X,_} = read_line_all2(File), - true - end || {_,File,X,_} <- All ], - ?line [ begin - io:format("read_line_all_alternating: ~s~n",[File]), - {Y,_} = read_line_all_alternating2(File), - true - end || {_,File,_,Y} <- All , Y =/= fail], - ?line [ begin - io:format("read_line_all_alternating (failing as should): ~s~n",[File]), - {'EXIT',_} = (catch read_line_all_alternating2(File)), - true - end || {_,File,_,Y} <- All , Y =:= fail], - ?line read_line_remove_files(All), + PrivDir = proplists:get_value(priv_dir, Config), + All = read_line_testdata(PrivDir), + read_line_create_files(All), + [ begin + io:format("read_line_all: ~s~n",[File]), + {X,_} = read_line_all2(File), + true + end || {_,File,X,_} <- All ], + [ begin + io:format("read_line_all_alternating: ~s~n",[File]), + {Y,_} = read_line_all_alternating2(File), + true + end || {_,File,_,Y} <- All , Y =/= fail], + [ begin + io:format("read_line_all_alternating (failing as should): ~s~n",[File]), + {'EXIT',_} = (catch read_line_all_alternating2(File)), + true + end || {_,File,_,Y} <- All , Y =:= fail], + read_line_remove_files(All), ok. -read_line_3(suite) -> - []; -read_line_3(doc) -> - ["read_line with raw file"]; +%% read_line with raw file. read_line_3(Config) when is_list(Config) -> - ?line PrivDir = ?config(priv_dir, Config), - ?line All = read_line_testdata(PrivDir), - ?line read_line_create_files(All), - ?line [ begin - io:format("read_line_all: ~s~n",[File]), - {X,_} = read_line_all3(File), - true - end || {_,File,X,_} <- All ], - ?line [ begin - io:format("read_line_all_alternating: ~s~n",[File]), - {Y,_} = read_line_all_alternating3(File), - true - end || {_,File,_,Y} <- All , Y =/= fail], - ?line [ begin - io:format("read_line_all_alternating (failing as should): ~s~n",[File]), - {'EXIT',_} = (catch read_line_all_alternating3(File)), - true - end || {_,File,_,Y} <- All , Y =:= fail], - ?line read_line_remove_files(All), + PrivDir = proplists:get_value(priv_dir, Config), + All = read_line_testdata(PrivDir), + read_line_create_files(All), + [ begin + io:format("read_line_all: ~s~n",[File]), + {X,_} = read_line_all3(File), + true + end || {_,File,X,_} <- All ], + [ begin + io:format("read_line_all_alternating: ~s~n",[File]), + {Y,_} = read_line_all_alternating3(File), + true + end || {_,File,_,Y} <- All , Y =/= fail], + [ begin + io:format("read_line_all_alternating (failing as should): ~s~n",[File]), + {'EXIT',_} = (catch read_line_all_alternating3(File)), + true + end || {_,File,_,Y} <- All , Y =:= fail], + read_line_remove_files(All), ok. -read_line_4(suite) -> - []; -read_line_4(doc) -> - ["read_line with raw buffered file"]; +%% read_line with raw buffered file. read_line_4(Config) when is_list(Config) -> - ?line PrivDir = ?config(priv_dir, Config), - ?line All = read_line_testdata(PrivDir), - ?line read_line_create_files(All), - ?line [ begin - io:format("read_line_all: ~s~n",[File]), - {X,_} = read_line_all4(File), - true - end || {_,File,X,_} <- All ], - ?line [ begin - io:format("read_line_all_alternating: ~s~n",[File]), - {Y,_} = read_line_all_alternating4(File), - true - end || {_,File,_,Y} <- All , Y =/= fail], - ?line [ begin - io:format("read_line_all_alternating (failing as should): ~s~n",[File]), - {'EXIT',_} = (catch read_line_all_alternating4(File)), - true - end || {_,File,_,Y} <- All , Y =:= fail], - ?line read_line_remove_files(All), + PrivDir = proplists:get_value(priv_dir, Config), + All = read_line_testdata(PrivDir), + read_line_create_files(All), + [ begin + io:format("read_line_all: ~s~n",[File]), + {X,_} = read_line_all4(File), + true + end || {_,File,X,_} <- All ], + [ begin + io:format("read_line_all_alternating: ~s~n",[File]), + {Y,_} = read_line_all_alternating4(File), + true + end || {_,File,_,Y} <- All , Y =/= fail], + [ begin + io:format("read_line_all_alternating (failing as should): ~s~n",[File]), + {'EXIT',_} = (catch read_line_all_alternating4(File)), + true + end || {_,File,_,Y} <- All , Y =:= fail], + read_line_remove_files(All), ok. rl_lines() -> @@ -4168,9 +4300,9 @@ read_line_create7(Filename) -> file:close(F). read_line_all(Filename) -> - {ok,F} = prim_file:open(Filename,[read,binary]), + {ok,F} = ?PRIM_FILE:open(Filename,[read,binary]), X=read_rl_lines(F), - prim_file:close(F), + ?PRIM_FILE:close(F), Bin = list_to_binary([B || {ok,B} <- X]), Bin = re:replace(list_to_binary([element(2,file:read_file(Filename))]), "\r\n","\n",[global,{return,binary}]), @@ -4203,7 +4335,7 @@ read_line_all4(Filename) -> {length(X),Bin}. read_rl_lines(F) -> - case prim_file:read_line(F) of + case ?PRIM_FILE:read_line(F) of eof -> []; {error,X} -> @@ -4223,9 +4355,9 @@ read_rl_lines2(F) -> end. read_line_all_alternating(Filename) -> - {ok,F} = prim_file:open(Filename,[read,binary]), + {ok,F} = ?PRIM_FILE:open(Filename,[read,binary]), X=read_rl_lines(F,true), - prim_file:close(F), + ?PRIM_FILE:close(F), Bin = list_to_binary([B || {ok,B} <- X]), Bin = re:replace(list_to_binary([element(2,file:read_file(Filename))]), "\r\n","\n",[global,{return,binary}]), @@ -4259,8 +4391,8 @@ read_line_all_alternating4(Filename) -> read_rl_lines(F,Alternate) -> case begin case Alternate of - true -> prim_file:read(F,1); - false -> prim_file:read_line(F) + true -> ?PRIM_FILE:read(F,1); + false -> ?PRIM_FILE:read_line(F) end end of eof -> @@ -4340,7 +4472,7 @@ run_large_file_test(Config, Run, Name) -> {{unix,sunos},OsVersion} when OsVersion < {5,5,1} -> {skip,"Only supported on Win32, Unix or SunOS >= 5.5.1"}; {{unix,_},_} -> - N = disc_free(?config(priv_dir, Config)), + N = disc_free(proplists:get_value(priv_dir, Config)), io:format("Free disk: ~w KByte~n", [N]), if N < 5 * (1 bsl 20) -> %% Less than 5 GByte free @@ -4354,9 +4486,9 @@ run_large_file_test(Config, Run, Name) -> do_run_large_file_test(Config, Run, Name0) -> - Name = filename:join(?config(priv_dir, Config), + Name = filename:join(proplists:get_value(priv_dir, Config), ?MODULE_STRING ++ Name0), - + %% Set up a process that will delete this file. Tester = self(), Deleter = @@ -4369,7 +4501,7 @@ do_run_large_file_test(Config, Run, Name0) -> end, ?FILE_MODULE:delete(Name) end), - + %% Run the test case. Res = Run(Name), diff --git a/lib/kernel/test/file_SUITE_data/realmen.html b/lib/kernel/test/file_SUITE_data/realmen.html index c810a5d088..92e13f23b8 100644 --- a/lib/kernel/test/file_SUITE_data/realmen.html +++ b/lib/kernel/test/file_SUITE_data/realmen.html @@ -237,7 +237,7 @@ destroy most of the interesting uses for EQUIVALENCE, and make it impossible to modify the operating system code with negative subscripts. Worst of all, bounds checking is inefficient. -<LI> Source code maintainance systems. A Real Programmer keeps his +<LI> Source code maintenance systems. A Real Programmer keeps his code locked up in a card file, because it implies that its owner cannot leave his important programs unguarded [5]. @@ -396,7 +396,7 @@ double stuff Oreos for special occasions. <LI> Underneath the Oreos is a flow-charting template, left there by the previous occupant of the office. (Real Programmers write programs, -not documentation. Leave that to the maintainence people.) +not documentation. Leave that to the maintenance people.) </UL> <P> diff --git a/lib/kernel/test/file_name_SUITE.erl b/lib/kernel/test/file_name_SUITE.erl index 9354af2e41..3afc647081 100644 --- a/lib/kernel/test/file_name_SUITE.erl +++ b/lib/kernel/test/file_name_SUITE.erl @@ -2,23 +2,24 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2013. All Rights Reserved. +%% Copyright Ericsson AB 1996-2017. 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/. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at %% -%% 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. +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -include_lib("kernel/include/file.hrl"). %% @@ -76,16 +77,17 @@ init_per_testcase/2, end_per_testcase/2]). -export([normal/1,icky/1,very_icky/1,normalize/1,home_dir/1]). +-define(PRIM_FILE, prim_file). init_per_testcase(_Func, Config) -> - Dog = test_server:timetrap(test_server:seconds(60)), - [{watchdog,Dog}|Config]. + Config. -end_per_testcase(_Func, Config) -> - Dog = ?config(watchdog, Config), - test_server:timetrap_cancel(Dog). +end_per_testcase(_Func, _Config) -> + ok. -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap,{minutes,1}}]. all() -> [normal, icky, very_icky, normalize, home_dir]. @@ -100,19 +102,16 @@ end_per_suite(_Config) -> ok. init_per_group(_GroupName, Config) -> - Config. + Config. end_per_group(_GroupName, Config) -> - Config. + Config. -home_dir(suite) -> - []; -home_dir(doc) -> - ["Check that Erlang can be started with unicode named home directory"]; +%% Check that Erlang can be started with unicode named home directory. home_dir(Config) when is_list(Config) -> try Name=[960,945,964,961,953,954], - Priv = ?config(priv_dir, Config), + Priv = proplists:get_value(priv_dir, Config), UniMode = file:native_name_encoding() =/= latin1, if not UniMode -> @@ -133,7 +132,7 @@ home_dir(Config) when is_list(Config) -> os:putenv("HOME",NewHome), {"HOME",Save}; _ -> - rm_rf(prim_file,NewHome), + rm_rf(?PRIM_FILE,NewHome), throw(unsupported_os) end, try @@ -141,8 +140,13 @@ home_dir(Config) when is_list(Config) -> test_server:stop_node(Node), ok after - os:putenv(SaveOldName,SaveOldValue), - rm_rf(prim_file,NewHome) + case SaveOldValue of + false -> + os:unsetenv(SaveOldName); + _ -> + os:putenv(SaveOldName,SaveOldValue) + end, + rm_rf(?PRIM_FILE,NewHome) end catch throw:need_unicode_mode -> @@ -153,49 +157,41 @@ home_dir(Config) when is_list(Config) -> {skipped,"Runs only on Unix/Windows"} end. -normalize(suite) -> - []; -normalize(doc) -> - ["Check that filename normalization works"]; +%% Check that filename normalization works. normalize(Config) when is_list(Config) -> - random:seed({1290,431421,830412}), + rand:seed(exsplus, {1290,431421,830412}), try - ?line UniMode = file:native_name_encoding() =/= latin1, + UniMode = file:native_name_encoding() =/= latin1, if not UniMode -> throw(need_unicode_mode); true -> ok end, - ?line Pairs = [rand_comp_decomp(200) || _ <- lists:seq(1,1000)], + Pairs = [rand_comp_decomp(200) || _ <- lists:seq(1,1000)], case os:type() of {unix,darwin} -> - ?line [ true = (A =:= prim_file:internal_native2name(B)) || + [ true = (A =:= prim_file:internal_native2name(B)) || {A,B} <- Pairs ]; _ -> ok end, - ?line [ true = (A =:= prim_file:internal_normalize_utf8(B)) || - {A,B} <- Pairs ] - + [ true = (A =:= prim_file:internal_normalize_utf8(B)) || + {A,B} <- Pairs ] + catch throw:need_unicode_mode -> io:format("Sorry, can only run in unicode mode.~n"), {skipped,"VM needs to be started in Unicode filename mode"} end. - -normal(suite) -> - []; -normal(doc) -> - "Check file operations on normal file names regardless of unicode mode"; + +%% Check file operations on normal file names regardless of unicode mode. normal(Config) when is_list(Config) -> {ok,Dir} = file:get_cwd(), try - Priv = ?config(priv_dir, Config), + Priv = proplists:get_value(priv_dir, Config), file:set_cwd(Priv), - put(file_module,prim_file), - ok = check_normal(prim_file), - put(file_module,file), + ok = check_normal(?PRIM_FILE), ok = check_normal(file), %% If all is good, delete dir again (avoid hanging dir on windows) rm_rf(file,"normal_dir"), @@ -203,12 +199,9 @@ normal(Config) when is_list(Config) -> after file:set_cwd(Dir) end. - -icky(suite) -> - []; -icky(doc) -> - "Check file operations on normal file names regardless of unicode mode"; + +%% Check file operations on normal file names regardless of unicode mode. icky(Config) when is_list(Config) -> case hopeless_darwin() of true -> @@ -216,11 +209,9 @@ icky(Config) when is_list(Config) -> false -> {ok,Dir} = file:get_cwd(), try - Priv = ?config(priv_dir, Config), + Priv = proplists:get_value(priv_dir, Config), file:set_cwd(Priv), - put(file_module,prim_file), - ok = check_icky(prim_file), - put(file_module,file), + ok = check_icky(?PRIM_FILE), ok = check_icky(file), %% If all is good, delete dir again (avoid hanging dir on windows) rm_rf(file,"icky_dir"), @@ -229,10 +220,7 @@ icky(Config) when is_list(Config) -> file:set_cwd(Dir) end end. -very_icky(suite) -> - []; -very_icky(doc) -> - "Check file operations on normal file names regardless of unicode mode"; +%% Check file operations on normal file names regardless of unicode mode. very_icky(Config) when is_list(Config) -> case hopeless_darwin() of true -> @@ -240,14 +228,12 @@ very_icky(Config) when is_list(Config) -> false -> {ok,Dir} = file:get_cwd(), try - Priv = ?config(priv_dir, Config), + Priv = proplists:get_value(priv_dir, Config), file:set_cwd(Priv), - put(file_module,prim_file), - case check_very_icky(prim_file) of + case check_very_icky(?PRIM_FILE) of need_unicode_mode -> {skipped,"VM needs to be started in Unicode filename mode"}; ok -> - put(file_module,file), ok = check_very_icky(file), %% If all is good, delete dir again %% (avoid hanging dir on windows) @@ -258,78 +244,76 @@ very_icky(Config) when is_list(Config) -> file:set_cwd(Dir) end end. - + check_normal(Mod) -> {ok,Dir} = Mod:get_cwd(), try - ?line make_normal_dir(Mod), - ?line {ok, L0} = Mod:list_dir("."), - ?line L1 = lists:sort(L0), - %erlang:display(L1), - ?line L1 = lists:sort(list(normal_dir())), - ?line {ok,D2} = Mod:get_cwd(), - ?line true = is_list(D2), - ?line case Mod:altname("fil1") of + NormalDir = make_normal_dir(Mod, "normal_dir"), + io:format("Normaldir = ~p\n", [NormalDir]), + L1 = lists:sort(list(NormalDir)), + {ok, L0} = Mod:list_dir("."), + io:format("L0 = ~p\n", [L0]), + L1 = lists:sort(L0), + {ok,D2} = Mod:get_cwd(), + true = is_list(D2), + case Mod:altname("fil1") of {error,enotsup} -> ok; {ok,LLL} when is_list(LLL) -> ok end, - ?line [ true = is_list(El) || El <- L1], - ?line Syms = [ {S,Targ,list_to_binary(get_data(Targ,normal_dir()))} - || {T,S,Targ} <- normal_dir(), T =:= symlink ], - ?line [ {ok, Cont} = Mod:read_file(SymL) || {SymL,_,Cont} <- Syms ], - ?line [ {ok, Targ} = fixlink(Mod:read_link(SymL)) || {SymL,Targ,_} <- Syms ], - ?line chk_cre_dir(Mod,[{directory,"temp_dir",normal_dir()}]), - ?line {ok,BeginAt} = Mod:get_cwd(), - ?line true = is_list(BeginAt), - ?line {error,enoent} = Mod:set_cwd("tmp_dir"), - ?line ok = Mod:set_cwd("temp_dir"), - ?line {ok, NowAt} = Mod:get_cwd(), - ?line true = BeginAt =/= NowAt, - ?line ok = Mod:set_cwd(".."), - ?line {ok,BeginAt} = Mod:get_cwd(), - ?line rm_r(Mod,"temp_dir"), - ?line true = is_list(Dir), - ?line [ true = is_list(FN) || FN <- L0 ], - case has_links() of - true -> - ?line ok = Mod:make_link("fil1","nisse"), - ?line {ok, <<"fil1">>} = Mod:read_file("nisse"), - ?line {ok, #file_info{type = regular}} = Mod:read_link_info("nisse"), - ?line ok = Mod:delete("nisse"), - ?line {ok, <<"fil1">>} = Mod:read_file("fil1"), - ?line {error,enoent} = Mod:read_file("nisse"), - ?line {error,enoent} = Mod:read_link_info("nisse"); - false -> + [ true = is_list(El) || El <- L1], + Syms = [ {S,Targ,list_to_binary(get_data(Targ, NormalDir))} + || {T,S,Targ} <- NormalDir, T =:= symlink ], + [ {ok, Cont} = Mod:read_file(SymL) || {SymL,_,Cont} <- Syms ], + [ {ok, Targ} = fixlink(Mod:read_link(SymL)) || {SymL,Targ,_} <- Syms ], + + {ok,BeginAt} = Mod:get_cwd(), + true = is_list(BeginAt), + TempDir = "temp_dir", + make_normal_dir(Mod, TempDir), + {error,enoent} = Mod:set_cwd("tmp_dir"), + {ok, NowAt} = Mod:get_cwd(), + true = BeginAt =/= NowAt, + ok = Mod:set_cwd(".."), + {ok,BeginAt} = Mod:get_cwd(), + rm_r(Mod, TempDir), + true = is_list(Dir), + [ true = is_list(FN) || FN <- L0 ], + case Mod:make_link("fil1","nisse") of + ok -> + {ok, <<"fil1">>} = Mod:read_file("nisse"), + {ok, #file_info{type = regular}} = Mod:read_link_info("nisse"), + ok = Mod:delete("nisse"), + {ok, <<"fil1">>} = Mod:read_file("fil1"), + {error,enoent} = Mod:read_file("nisse"), + {error,enoent} = Mod:read_link_info("nisse"); + {error,enotsup} -> ok end, - ?line [ begin - ?line {ok, FD} = Mod:open(Name,[read]), - ?line {ok, Content} = Mod:read(FD,1024), - ?line ok = file:close(FD) - end || {regular,Name,Content} <- normal_dir() ], - ?line [ begin - ?line {ok, FD} = Mod:open(Name,[read,binary]), - ?line BC = list_to_binary(Content), - ?line {ok, BC} = Mod:read(FD,1024), - ?line ok = file:close(FD) - end || {regular,Name,Content} <- normal_dir() ], - ?line Mod:rename("fil1","tmp_fil1"), - ?line {ok, <<"fil1">>} = Mod:read_file("tmp_fil1"), - ?line {error,enoent} = Mod:read_file("fil1"), - ?line Mod:rename("tmp_fil1","fil1"), - ?line {ok, <<"fil1">>} = Mod:read_file("fil1"), - ?line {error,enoent} = Mod:read_file("tmp_fil1"), - ?line {ok,FI} = Mod:read_file_info("fil1"), - ?line NewMode = FI#file_info.mode band (bnot 8#333), - ?line NewMode2 = NewMode bor 8#222, - ?line true = NewMode2 =/= NewMode, - ?line ok = Mod:write_file_info("fil1",FI#file_info{mode = NewMode}), - ?line {ok,#file_info{mode = NewMode}} = Mod:read_file_info("fil1"), - ?line ok = Mod:write_file_info("fil1",FI#file_info{mode = NewMode2}), - ?line {ok,#file_info{mode = NewMode2}} = Mod:read_file_info("fil1"), + [ begin + {ok, FD} = Mod:open(Name,[read,binary]), + BC = list_to_binary(Content), + {ok, BC} = Mod:read(FD,1024), + ok = file:close(FD) + end || {regular,Name,Content} <- NormalDir ], + {error, badarg} = Mod:rename("fil1\0tmp_fil2","tmp_fil1"), + Mod:rename("fil1","tmp_fil1"), + {error, badarg} = Mod:read_file("tmp_fil1\0.txt"), + {ok, <<"fil1">>} = Mod:read_file("tmp_fil1"), + {error,enoent} = Mod:read_file("fil1"), + Mod:rename("tmp_fil1","fil1"), + {ok, <<"fil1">>} = Mod:read_file("fil1"), + {error,enoent} = Mod:read_file("tmp_fil1"), + {ok,FI} = Mod:read_file_info("fil1"), + NewMode = FI#file_info.mode band (bnot 8#333), + NewMode2 = NewMode bor 8#222, + true = NewMode2 =/= NewMode, + ok = Mod:write_file_info("fil1",FI#file_info{mode = NewMode}), + {ok,#file_info{mode = NewMode}} = Mod:read_file_info("fil1"), + ok = Mod:write_file_info("fil1",FI#file_info{mode = NewMode2}), + {ok,#file_info{mode = NewMode2}} = Mod:read_file_info("fil1"), ok after case Mod:read_file_info("fil1") of @@ -346,129 +330,125 @@ check_normal(Mod) -> check_icky(Mod) -> {ok,Dir} = Mod:get_cwd(), try - ?line true=(length("åäö") =:= 3), - ?line UniMode = file:native_name_encoding() =/= latin1, - ?line make_icky_dir(Mod), + true=(length("åäö") =:= 3), + UniMode = file:native_name_encoding() =/= latin1, + IckyDir = make_icky_dir(Mod, "icky_dir"), {ok, L0} = Mod:list_dir_all("."), - ?line L1 = lists:sort(L0), - io:format("~p~n~p~n~n",[L1,lists:sort(list(icky_dir()))]), - ?line L1 = lists:sort(convlist(list(icky_dir()))), - ?line {ok,D2} = Mod:get_cwd(), - ?line true = is_list(D2), -%% Altname only on windows, and there are no non native filenames there -%% ?line case Mod:altname("fil1") of -%% {error,enotsup} -> -%% ok; -%% {ok,LLL} when is_list(LLL) -> -%% ok -%% end, - ?line [ true = ((is_list(El) or (UniMode and is_binary(El)))) || El <- L1], - ?line Syms = [ {S,conv(Targ),list_to_binary(get_data(Targ,icky_dir()))} - || {T,S,Targ} <- icky_dir(), T =:= symlink ], - ?line [ {ok, Cont} = Mod:read_file(SymL) || {SymL,_,Cont} <- Syms ], + L1 = lists:sort(L0), + io:format("~p~n~p~n~n",[L1,lists:sort(list(IckyDir))]), + L1 = lists:sort(convlist(list(IckyDir))), + {ok,D2} = Mod:get_cwd(), + true = is_list(D2), + %% Altname only on windows, and there are no non native filenames there + %% case Mod:altname("fil1") of + %% {error,enotsup} -> + %% ok; + %% {ok,LLL} when is_list(LLL) -> + %% ok + %% end, + [ true = ((is_list(El) or (UniMode and is_binary(El)))) || El <- L1], + Syms = [ {S,conv(Targ),list_to_binary(get_data(Targ,IckyDir))} + || {T,S,Targ} <- IckyDir, T =:= symlink ], + [ {ok, Cont} = Mod:read_file(SymL) || {SymL,_,Cont} <- Syms ], [ {ok, Targ} = fixlink(Mod:read_link_all(SymL)) || {SymL,Targ,_} <- Syms ], - ?line chk_cre_dir(Mod,[{directory,"åäö_dir",icky_dir()}]), - ?line {ok,BeginAt} = Mod:get_cwd(), - ?line true = is_list(BeginAt), - ?line {error,enoent} = Mod:set_cwd("åä_dir"), - ?line ok = Mod:set_cwd("åäö_dir"), - ?line {ok, NowAt} = Mod:get_cwd(), - ?line true = is_list(NowAt), - ?line true = BeginAt =/= NowAt, - ?line ok = Mod:set_cwd(".."), - ?line {ok,BeginAt} = Mod:get_cwd(), - ?line rm_r2(Mod,"åäö_dir"), + + {ok,BeginAt} = Mod:get_cwd(), + true = is_list(BeginAt), + _ = make_icky_dir(Mod, "åäö_dir"), + {error,enoent} = Mod:set_cwd("åä_dir"), + {ok, NowAt} = Mod:get_cwd(), + true = is_list(NowAt), + true = BeginAt =/= NowAt, + ok = Mod:set_cwd(".."), + {ok,BeginAt} = Mod:get_cwd(), + rm_r2(Mod,"åäö_dir"), {OS,_} = os:type(), - % Check that treat_icky really converts to the same as the OS + + %% Check that treat_icky really converts to the same as the OS case UniMode of true -> - ?line chk_cre_dir(Mod,[{directory,"åäö_dir",[]}]), - ?line ok = Mod:set_cwd("åäö_dir"), - ?line ok = Mod:write_file(<<"ååå">>,<<"hello">>), - ?line Treated = treat_icky(<<"ååå">>), + ok = Mod:make_dir("åäö_dir"), + ok = Mod:set_cwd("åäö_dir"), + ok = Mod:write_file(<<"ååå">>,<<"hello">>), + Treated = treat_icky(<<"ååå">>), {ok,[Treated]} = Mod:list_dir_all("."), - ?line ok = Mod:delete(<<"ååå">>), - ?line {ok,[]} = Mod:list_dir("."), - ?line ok = Mod:set_cwd(".."), - ?line rm_r2(Mod,"åäö_dir"); + ok = Mod:delete(<<"ååå">>), + {ok,[]} = Mod:list_dir("."), + ok = Mod:set_cwd(".."), + rm_r2(Mod,"åäö_dir"); false -> ok end, - ?line chk_cre_dir(Mod,[{directory,treat_icky(<<"åäö_dir">>),icky_dir()}]), + _ = make_icky_dir(Mod, treat_icky(<<"åäö_dir"/utf8>>)), if UniMode and (OS =/= win32) -> - ?line {error,enoent} = Mod:set_cwd("åäö_dir"); + {error,enoent} = Mod:set_cwd("åäö_dir"); true -> ok end, - ?line {ok,BeginAt} = Mod:get_cwd(), - case has_links() of - true -> - ?line ok = Mod:make_link("fil1","nisseö"), - ?line {ok, <<"fil1">>} = Mod:read_file("nisseö"), - ?line {ok, #file_info{type = regular}} = Mod:read_link_info("nisseö"), - ?line ok = Mod:delete("nisseö"), - ?line ok = Mod:make_link("fil1",treat_icky(<<"nisseö">>)), - ?line {ok, <<"fil1">>} = Mod:read_file(treat_icky(<<"nisseö">>)), - ?line {ok, #file_info{type = regular}} = Mod:read_link_info(treat_icky(<<"nisseö">>)), - ?line ok = Mod:delete(treat_icky(<<"nisseö">>)), - ?line {ok, <<"fil1">>} = Mod:read_file("fil1"), - ?line {error,enoent} = Mod:read_file("nisseö"), - ?line {error,enoent} = Mod:read_link_info("nisseö"), - ?line {error,enoent} = Mod:read_file(treat_icky(<<"nisseö">>)), - ?line {error,enoent} = Mod:read_link_info(treat_icky(<<"nisseö">>)); - false -> + ok = Mod:set_cwd(".."), + {ok,BeginAt} = Mod:get_cwd(), + case Mod:make_link("fil1", "nisseö") of + ok -> + {ok, <<"fil1">>} = Mod:read_file("nisseö"), + {ok, #file_info{type = regular}} = Mod:read_link_info("nisseö"), + ok = Mod:delete("nisseö"), + ok = Mod:make_link("fil1",treat_icky(<<"nisseö">>)), + {ok, <<"fil1">>} = Mod:read_file(treat_icky(<<"nisseö">>)), + {ok, #file_info{type = regular}} = Mod:read_link_info(treat_icky(<<"nisseö">>)), + ok = Mod:delete(treat_icky(<<"nisseö">>)), + {ok, <<"fil1">>} = Mod:read_file("fil1"), + {error,enoent} = Mod:read_file("nisseö"), + {error,enoent} = Mod:read_link_info("nisseö"), + {error,enoent} = Mod:read_file(treat_icky(<<"nisseö">>)), + {error,enoent} = Mod:read_link_info(treat_icky(<<"nisseö">>)); + {error,enotsup} -> ok end, - ?line [ begin - ?line {ok, FD} = Mod:open(Name,[read]), - ?line {ok, Content} = Mod:read(FD,1024), - ?line ok = file:close(FD) - end || {regular,Name,Content} <- icky_dir() ], - ?line [ begin - ?line {ok, FD} = Mod:open(Name,[read,binary]), - ?line BC = list_to_binary([Content]), - ?line {ok, BC} = Mod:read(FD,1024), - ?line ok = file:close(FD) - end || {regular,Name,Content} <- icky_dir() ], - ?line Mod:rename("åäö2","åäö_fil1"), - ?line {ok, <<"åäö2">>} = Mod:read_file("åäö_fil1"), - ?line {error,enoent} = Mod:read_file("åäö2"), - ?line Mod:rename("åäö_fil1","åäö2"), - ?line {ok, <<"åäö2">>} = Mod:read_file("åäö2"), - ?line {error,enoent} = Mod:read_file("åäö_fil1"), + [ begin + {ok, FD} = Mod:open(Name,[read,binary]), + BC = list_to_binary([Content]), + {ok, BC} = Mod:read(FD,1024), + ok = file:close(FD) + end || {regular,Name,Content} <- IckyDir ], + Mod:rename("åäö2","åäö_fil1"), + {ok, <<"åäö2">>} = Mod:read_file("åäö_fil1"), + {error,enoent} = Mod:read_file("åäö2"), + Mod:rename("åäö_fil1","åäö2"), + {ok, <<"åäö2">>} = Mod:read_file("åäö2"), + {error,enoent} = Mod:read_file("åäö_fil1"), - ?line Mod:rename("åäö2",treat_icky(<<"åäö_fil1">>)), - ?line {ok, <<"åäö2">>} = Mod:read_file(treat_icky(<<"åäö_fil1">>)), + Mod:rename("åäö2",treat_icky(<<"åäö_fil1">>)), + {ok, <<"åäö2">>} = Mod:read_file(treat_icky(<<"åäö_fil1">>)), if UniMode and (OS =/= win32) -> {error,enoent} = Mod:read_file("åäö_fil1"); true -> ok end, - ?line {error,enoent} = Mod:read_file("åäö2"), - ?line Mod:rename(treat_icky(<<"åäö_fil1">>),"åäö2"), - ?line {ok, <<"åäö2">>} = Mod:read_file("åäö2"), - ?line {error,enoent} = Mod:read_file("åäö_fil1"), - ?line {error,enoent} = Mod:read_file(treat_icky(<<"åäö_fil1">>)), + {error,enoent} = Mod:read_file("åäö2"), + Mod:rename(treat_icky(<<"åäö_fil1">>),"åäö2"), + {ok, <<"åäö2">>} = Mod:read_file("åäö2"), + {error,enoent} = Mod:read_file("åäö_fil1"), + {error,enoent} = Mod:read_file(treat_icky(<<"åäö_fil1">>)), - ?line {ok,FI} = Mod:read_file_info("åäö2"), - ?line NewMode = FI#file_info.mode band (bnot 8#333), - ?line NewMode2 = NewMode bor 8#222, - ?line true = NewMode2 =/= NewMode, - ?line ok = Mod:write_file_info("åäö2",FI#file_info{mode = NewMode}), - ?line {ok,#file_info{mode = NewMode}} = Mod:read_file_info("åäö2"), - ?line ok = Mod:write_file_info("åäö2",FI#file_info{mode = NewMode2}), - ?line {ok,#file_info{mode = NewMode2}} = Mod:read_file_info("åäö2"), + {ok,FI} = Mod:read_file_info("åäö2"), + NewMode = FI#file_info.mode band (bnot 8#333), + NewMode2 = NewMode bor 8#222, + true = NewMode2 =/= NewMode, + ok = Mod:write_file_info("åäö2",FI#file_info{mode = NewMode}), + {ok,#file_info{mode = NewMode}} = Mod:read_file_info("åäö2"), + ok = Mod:write_file_info("åäö2",FI#file_info{mode = NewMode2}), + {ok,#file_info{mode = NewMode2}} = Mod:read_file_info("åäö2"), - ?line {ok,FII} = Mod:read_file_info(treat_icky(<<"åäö5">>)), - ?line true = NewMode2 =/= NewMode, - ?line ok = Mod:write_file_info(treat_icky(<<"åäö5">>),FII#file_info{mode = NewMode}), - ?line {ok,#file_info{mode = NewMode}} = Mod:read_file_info(treat_icky(<<"åäö5">>)), - ?line ok = Mod:write_file_info(<<"åäö5">>,FII#file_info{mode = NewMode2}), - ?line {ok,#file_info{mode = NewMode2}} = Mod:read_file_info(treat_icky(<<"åäö5">>)), + {ok,FII} = Mod:read_file_info(treat_icky(<<"åäö5">>)), + true = NewMode2 =/= NewMode, + ok = Mod:write_file_info(treat_icky(<<"åäö5">>),FII#file_info{mode = NewMode}), + {ok,#file_info{mode = NewMode}} = Mod:read_file_info(treat_icky(<<"åäö5">>)), + ok = Mod:write_file_info(<<"åäö5">>,FII#file_info{mode = NewMode2}), + {ok,#file_info{mode = NewMode2}} = Mod:read_file_info(treat_icky(<<"åäö5">>)), ok after Mod:set_cwd(Dir), @@ -478,90 +458,85 @@ check_icky(Mod) -> check_very_icky(Mod) -> {ok,Dir} = Mod:get_cwd(), try - ?line true=(length("åäö") =:= 3), - ?line UniMode = file:native_name_encoding() =/= latin1, + true=(length("åäö") =:= 3), + UniMode = file:native_name_encoding() =/= latin1, if not UniMode -> throw(need_unicode_mode); true -> ok end, - ?line make_very_icky_dir(Mod), - {ok, L0} = Mod:list_dir_all("."), - ?line L1 = lists:sort(L0), - ?line L1 = lists:sort(convlist(list(very_icky_dir()))), - ?line {ok,D2} = Mod:get_cwd(), - ?line true = is_list(D2), - ?line [ true = ((is_list(El) or is_binary(El))) || El <- L1], - ?line Syms = [ {S,conv(Targ),list_to_binary(get_data(Targ,very_icky_dir()))} - || {T,S,Targ} <- very_icky_dir(), T =:= symlink ], - ?line [ {ok, Cont} = Mod:read_file(SymL) || {SymL,_,Cont} <- Syms ], - ?line [ {ok, Targ} = fixlink(Mod:read_link_all(SymL)) || - {SymL,Targ,_} <- Syms ], - ?line chk_cre_dir(Mod,[{directory,[1088,1079,1091]++"_dir",very_icky_dir()}]), - ?line {ok,BeginAt} = Mod:get_cwd(), - ?line true = is_list(BeginAt), - ?line {error,enoent} = Mod:set_cwd("åä_dir"), - ?line ok = Mod:set_cwd([1088,1079,1091]++"_dir"), - ?line {ok, NowAt} = Mod:get_cwd(), - ?line true = is_list(NowAt), - ?line true = BeginAt =/= NowAt, - ?line ok = Mod:set_cwd(".."), - ?line {ok,BeginAt} = Mod:get_cwd(), - ?line rm_r2(Mod,[1088,1079,1091]++"_dir"), + VeryIckyDir = make_very_icky_dir(Mod, "very_icky_dir"), + Expected = lists:sort(convlist(list(VeryIckyDir))), + {ok, Actual} = Mod:list_dir_all("."), + Expected = lists:sort(Actual), + {ok,D2} = Mod:get_cwd(), + true = is_list(D2), + [ true = ((is_list(El) or is_binary(El))) || El <- Expected], + Syms = [{S,conv(Targ),list_to_binary(get_data(Targ, VeryIckyDir))} + || {symlink,S,Targ} <- VeryIckyDir], + [ {ok, Cont} = Mod:read_file(SymL) || {SymL,_,Cont} <- Syms ], + [ {ok, Targ} = fixlink(Mod:read_link_all(SymL)) || + {SymL,Targ,_} <- Syms ], - case has_links() of - true -> - ?line ok = Mod:make_link("fil1","nisse"++[1088,1079,1091]), - ?line {ok, <<"fil1">>} = + {ok,BeginAt} = Mod:get_cwd(), + OtherDir = [1088,1079,1091] ++ "_dir", + true = is_list(BeginAt), + make_very_icky_dir(Mod, OtherDir), + {error,enoent} = Mod:set_cwd("åä_dir"), + {ok, NowAt} = Mod:get_cwd(), + true = is_list(NowAt), + true = BeginAt =/= NowAt, + ok = Mod:set_cwd(".."), + {ok,BeginAt} = Mod:get_cwd(), + rm_r2(Mod, OtherDir), + + case Mod:make_link("fil1","nisse"++[1088,1079,1091]) of + ok -> + {ok, <<"fil1">>} = Mod:read_file("nisse"++[1088,1079,1091]), - ?line {ok, #file_info{type = regular}} = + {ok, #file_info{type = regular}} = Mod:read_link_info("nisse"++[1088,1079,1091]), - ?line ok = Mod:delete("nisse"++[1088,1079,1091]), - ?line ok = Mod:make_link("fil1",<<"nisseö">>), - ?line {ok, <<"fil1">>} = Mod:read_file(<<"nisseö">>), - ?line {ok, #file_info{type = regular}} = + ok = Mod:delete("nisse"++[1088,1079,1091]), + ok = Mod:make_link("fil1",<<"nisseö">>), + {ok, <<"fil1">>} = Mod:read_file(<<"nisseö">>), + {ok, #file_info{type = regular}} = Mod:read_link_info(<<"nisseö">>), - ?line ok = Mod:delete(<<"nisseö">>), - ?line {ok, <<"fil1">>} = Mod:read_file("fil1"), - ?line {error,enoent} = Mod:read_file("nisse"++[1088,1079,1091]), - ?line {error,enoent} = Mod:read_link_info("nisse"++[1088,1079,1091]), - ?line {error,enoent} = Mod:read_file(<<"nisseö">>), - ?line {error,enoent} = Mod:read_link_info(<<"nisseö">>); - false -> + ok = Mod:delete(<<"nisseö">>), + {ok, <<"fil1">>} = Mod:read_file("fil1"), + {error,enoent} = Mod:read_file("nisse"++[1088,1079,1091]), + {error,enoent} = Mod:read_link_info("nisse"++[1088,1079,1091]), + {error,enoent} = Mod:read_file(<<"nisseö">>), + {error,enoent} = Mod:read_link_info(<<"nisseö">>); + {error,enotsup} -> ok end, - ?line [ begin - ?line {ok, FD} = Mod:open(Name,[read]), - ?line {ok, Content} = Mod:read(FD,1024), - ?line ok = file:close(FD) - end || {regular,Name,Content} <- very_icky_dir() ], - ?line [ begin - ?line {ok, FD} = Mod:open(Name,[read,binary]), - ?line BC = list_to_binary([Content]), - ?line {ok, BC} = Mod:read(FD,1024), - ?line ok = file:close(FD) - end || {regular,Name,Content} <- very_icky_dir() ], - ?line Mod:rename([956,965,963,954,959,49], - [956,965,963,954,959]++"_fil1"), - ?line {ok, <<"åäö2">>} = Mod:read_file([956,965,963,954,959]++"_fil1"), - ?line {error,enoent} = Mod:read_file([956,965,963,954,959,49]), - ?line Mod:rename([956,965,963,954,959]++"_fil1",[956,965,963,954,959,49]), - ?line {ok, <<"åäö2">>} = Mod:read_file([956,965,963,954,959,49]), - ?line {error,enoent} = Mod:read_file([956,965,963,954,959]++"_fil1"), + [ begin + {ok, FD} = Mod:open(Name,[read,binary]), + BC = list_to_binary([Content]), + {ok, BC} = Mod:read(FD,1024), + ok = file:close(FD) + end || {regular,Name,Content} <- VeryIckyDir ], + Mod:rename([956,965,963,954,959,49], + [956,965,963,954,959]++"_fil1"), + {ok, <<"åäö2">>} = Mod:read_file([956,965,963,954,959]++"_fil1"), + {error,enoent} = Mod:read_file([956,965,963,954,959,49]), + Mod:rename([956,965,963,954,959]++"_fil1",[956,965,963,954,959,49]), + {ok, <<"åäö2">>} = Mod:read_file([956,965,963,954,959,49]), + {error,enoent} = Mod:read_file([956,965,963,954,959]++"_fil1"), - ?line {ok,FI} = Mod:read_file_info([956,965,963,954,959,49]), - ?line NewMode = FI#file_info.mode band (bnot 8#333), - ?line NewMode2 = NewMode bor 8#222, - ?line true = NewMode2 =/= NewMode, - ?line ok = Mod:write_file_info([956,965,963,954,959,49], - FI#file_info{mode = NewMode}), - ?line {ok,#file_info{mode = NewMode}} = - Mod:read_file_info([956,965,963,954,959,49]), - ?line ok = Mod:write_file_info([956,965,963,954,959,49], - FI#file_info{mode = NewMode2}), - ?line {ok,#file_info{mode = NewMode2}} = - Mod:read_file_info([956,965,963,954,959,49]), + {ok,FI} = Mod:read_file_info([956,965,963,954,959,49]), + NewMode = FI#file_info.mode band (bnot 8#333), + NewMode2 = NewMode bor 8#222, + true = NewMode2 =/= NewMode, + ok = Mod:write_file_info([956,965,963,954,959,49], + FI#file_info{mode = NewMode}), + {ok,#file_info{mode = NewMode}} = + Mod:read_file_info([956,965,963,954,959,49]), + ok = Mod:write_file_info([956,965,963,954,959,49], + FI#file_info{mode = NewMode2}), + {ok,#file_info{mode = NewMode2}} = + Mod:read_file_info([956,965,963,954,959,49]), ok catch throw:need_unicode_mode -> @@ -591,7 +566,6 @@ rm_rf(Mod,Dir) -> end. rm_r(Mod,Dir) -> - %erlang:display({rm_r,Dir}), case Mod:read_link_info(Dir) of {ok, #file_info{type = directory}} -> {ok,#file_info{type = directory}} = Mod:read_file_info(Dir), @@ -609,7 +583,7 @@ rm_r(Mod,Dir) -> end. %% For icky test, allow binaries sometimes rm_r2(Mod,Dir) -> - %erlang:display({rm_r2,Dir}), + %% erlang:display({rm_r2,Dir}), case Mod:read_link_info(Dir) of {ok, #file_info{type = directory}} -> {ok,#file_info{type = directory}} = Mod:read_file_info(Dir), @@ -626,90 +600,35 @@ rm_r2(Mod,Dir) -> {ok, #file_info{type = symlink}} -> ok = Mod:delete(Dir) end. -chk_cre_dir(_,[]) -> - ok; -chk_cre_dir(Mod,[{regular,Name,Content}|T]) -> - %io:format("~p~n",[Name]), - ok = Mod:write_file(Name,Content), - chk_cre_dir(Mod,T); -chk_cre_dir(Mod,[{link,Name,Target}|T]) -> - ok = Mod:make_link(Target,Name), - chk_cre_dir(Mod,T); -chk_cre_dir(Mod,[{symlink,Name,Target}|T]) -> - ok = Mod:make_symlink(Target,Name), - chk_cre_dir(Mod,T); -chk_cre_dir(Mod,[{directory,Name,Content}|T]) -> - ok = Mod:make_dir(Name), - %io:format("Content = ~p~n",[Content]), - Content2 = [{Ty,filename:join(Name,N),case Ty of link -> filename:join(Name,C); _ -> C end} || {Ty,N,C} <- Content ], - %io:format("Content2 = ~p~n",[Content2]), - chk_cre_dir(Mod,Content2), - chk_cre_dir(Mod,T). - -has_links() -> - case os:type() of - {win32,_} -> - case os:version() of - {N,NN,_} when (N > 5) andalso (NN >= 1) -> - true; - _ -> - false - end; - _ -> - true - end. - -make_normal_dir(Mod) -> - rm_rf(Mod,"normal_dir"), - Mod:make_dir("normal_dir"), - Mod:set_cwd("normal_dir"), - Mod:write_file("fil1","fil1"), - Mod:write_file("fil2","fil2"), - case has_links() of - true -> - Mod:make_link("fil2","fil3"), - Mod:make_symlink("fil2","fil4"); - _ -> - ok - end, - Mod:make_dir("subdir"), - Mod:write_file(filename:join("subdir","subfil1"),"subfil1"), - ok. - -normal_dir() -> - [{regular,"fil1","fil1"}, - {regular,"fil2","fil2"}] ++ - case has_links() of - true -> - [{regular,"fil3","fil2"}, - {symlink,"fil4","fil2"}]; - false -> - [] - end ++ - [{directory,"subdir", - [{regular,"subfil1","subfil1"}]}]. -make_icky_dir(Mod) -> - rm_rf(Mod,"icky_dir"), - Icky=icky_dir(), - chk_cre_dir(Mod,[{directory,"icky_dir",linkify([],Icky)}]), - Mod:set_cwd("icky_dir"), - ok. +make_normal_dir(Mod, DirName) -> + Dir = [{regular,"fil1","fil1"}, + {regular,"fil2","fil2"}, + {hardlink,"fil3","fil2"}, + {symlink,"fil4","fil2"}, + {directory,"subdir", + [{regular,"subfil1","subfil1"}]}], + rm_rf(Mod, DirName), + Mod:make_dir(DirName), + Mod:set_cwd(DirName), + make_dir_contents(Dir, Mod). -linkify(_Passed,[]) -> - []; -linkify(Passed,[{regular,Name,Content}|T]) -> - Regulars = [ {N,C} || {regular,N,C} <- Passed, N =/= Name ], - case lists:keysearch(Content,2,Regulars) of - {value, {Linkto, Content}} -> - [{link,Name,Linkto} | linkify(Passed,T)]; - _ -> - [{regular,Name,Content} | linkify([{regular,Name,Content}|Passed],T)] - end; -linkify(Passed,[{directory, Name, Content}|T]) -> - [{directory,Name, linkify(Content,Content)}|linkify(Passed,T)]; -linkify(Passed,[H|T]) -> - [H|linkify([H|Passed],T)]. +make_icky_dir(Mod, IckyDirName) -> + Icky = [{regular,"fil1","fil1"}, + {regular,"åäö2","åäö2"}, + {hardlink,"åäö3","åäö2"}, + {symlink,"åäö4","åäö2"}, + {regular,treat_icky(<<"åäö5">>),"åäö5"}, + {symlink,treat_icky(<<"åäö6">>),treat_icky(<<"åäö5">>)}, + {directory,treat_icky(<<"åäösubdir2">>), + [{regular,treat_icky(<<"åäösubfil2">>),"åäösubfil12"}, + {regular,"åäösubfil3","åäösubfil13"}]}, + {directory,"åäösubdir", + [{regular,"åäösubfil1","åäösubfil1"}]}], + rm_rf(Mod, IckyDirName), + ok = Mod:make_dir(IckyDirName), + ok = Mod:set_cwd(IckyDirName), + make_dir_contents(Icky, Mod). hopeless_darwin() -> case {os:type(),os:version()} of @@ -719,58 +638,24 @@ hopeless_darwin() -> false end. -icky_dir() -> - [{regular,"fil1","fil1"}, - {regular,"åäö2","åäö2"}] ++ - case has_links() of - true -> - [{regular,"åäö3","åäö2"}, - {symlink,"åäö4","åäö2"}]; - false -> - [] - end ++ - [{regular,treat_icky(<<"åäö5">>),"åäö5"}] ++ - case has_links() of - true -> - [{symlink,treat_icky(<<"åäö6">>),treat_icky(<<"åäö5">>)}]; - false -> - [] - end ++ - [{directory,treat_icky(<<"åäösubdir2">>), - [{regular,treat_icky(<<"åäösubfil2">>),"åäösubfil12"}, - {regular,"åäösubfil3","åäösubfil13"}]}, - {directory,"åäösubdir", - [{regular,"åäösubfil1","åäösubfil1"}]}]. - -make_very_icky_dir(Mod) -> - rm_rf(Mod,"very_icky_dir"), - Icky=very_icky_dir(), - chk_cre_dir(Mod,[{directory,"very_icky_dir",linkify([],Icky)}]), - Mod:set_cwd("very_icky_dir"), - ok. - -very_icky_dir() -> - [{regular,"fil1","fil1"}, - {regular,[956,965,963,954,959,49],"åäö2"}] ++ - case has_links() of - true -> - [{regular,[956,965,963,954,959,50],"åäö2"}, - {symlink,[956,965,963,954,959,51],[956,965,963,954,959,49]}]; - false -> - [] - end ++ - [{regular,treat_icky(<<"åäö5">>),"åäö5"}] ++ - case has_links() of - true -> - [{symlink,treat_icky(<<"åäö6">>),treat_icky(<<"åäö5">>)}]; - false -> - [] - end ++ - [{directory,treat_icky(<<"åäösubdir2">>), - [{regular,treat_icky(<<"åäösubfil2">>),"åäösubfil12"}, - {regular,"åäösubfil3","åäösubfil13"}]}, - {directory,[956,965,963,954,959]++"subdir1", - [{regular,[956,965,963,954,959]++"subfil1","åäösubfil1"}]}]. +make_very_icky_dir(Mod, DirName) -> + Desc = [{regular,"fil1","fil1"}, + {regular,[956,965,963,954,959,49],"åäö2"}, + {hardlink,[956,965,963,954,959,50], + [956,965,963,954,959,49], + "åäö2"}, + {symlink,[956,965,963,954,959,51],[956,965,963,954,959,49]}, + {regular,treat_icky(<<"åäö5">>),"åäö5"}, + {symlink,treat_icky(<<"åäö6">>),treat_icky(<<"åäö5">>)}, + {directory,treat_icky(<<"åäösubdir2">>), + [{regular,treat_icky(<<"åäösubfil2">>),"åäösubfil12"}, + {regular,"åäösubfil3","åäösubfil13"}]}, + {directory,[956,965,963,954,959]++"subdir1", + [{regular,[956,965,963,954,959]++"subfil1","åäösubfil1"}]}], + rm_rf(Mod, DirName), + ok = Mod:make_dir(DirName), + ok = Mod:set_cwd(DirName), + make_dir_contents(Desc, Mod). %% Some OS'es simply do not allow non UTF8 filenames treat_icky(Bin) -> @@ -783,7 +668,7 @@ treat_icky(Bin) -> Bin end. -% Handle windows having absolute soft link targets. +%% Handle windows having absolute soft link targets. fixlink({ok,Link}) -> case os:type() of {win32,_} -> @@ -810,7 +695,7 @@ list([]) -> []; list([{_,Name,_} | T]) -> [Name | list(T)]. - + get_data(FN,List) -> case lists:keysearch(FN,2,List) of @@ -826,7 +711,7 @@ get_data(FN,List) -> convlist(L) -> convlist(file:native_name_encoding(),L). convlist(latin1,[Bin|T]) when is_binary(Bin) -> - %erlang:display('Convert...'), + %% erlang:display('Convert...'), [binary_to_list(Bin)| convlist(latin1,T)]; convlist(Any,[H|T]) -> [H|convlist(Any,T)]; @@ -843,18 +728,60 @@ conv(L) -> end. +make_dir_contents([{regular,Name,Contents}=H|T], Mod) -> + ok = Mod:write_file(Name, Contents), + [H|make_dir_contents(T, Mod)]; +make_dir_contents([{hardlink,Target,Name}|T], Mod) -> + case Mod:make_link(Name, Target) of + ok -> + [{regular,Target,Name}|make_dir_contents(T, Mod)]; + {error,enotsup} -> + make_dir_contents(T, Mod) + end; +make_dir_contents([{hardlink,Target,Name,Contents}|T], Mod) -> + case Mod:make_link(Name, Target) of + ok -> + [{regular,Target,Contents}|make_dir_contents(T, Mod)]; + {error,enotsup} -> + make_dir_contents(T, Mod) + end; +make_dir_contents([{symlink,Target,Name}=H|T], Mod) -> + case Mod:make_symlink(Name, Target) of + ok -> + [H|make_dir_contents(T, Mod)]; + {error,enotsup} -> + make_dir_contents(T, Mod); + {error,eperm} -> + make_dir_contents(T, Mod) + end; +make_dir_contents([{directory,Dir,C0}|T], Mod) -> + ok = Mod:make_dir(Dir), + C1 = [case Op of + Link when Link =:= hardlink; Link =:= symlink -> + {Op,filename:join(Dir, Name0),filename:join(Dir, Extra)}; + _ -> + {Op,filename:join(Dir, Name0),Extra} + end || {Op,Name0,Extra} <- C0], + C2 = make_dir_contents(C1, Mod), + C = [{Op,filename:basename(Name0),Extra} || + {Op,Name0,Extra} <- C2], + [{directory,Dir,C}|make_dir_contents(T, Mod)]; +make_dir_contents([], _Mod) -> + []. + + rand_comp_decomp(Max) -> - N = random:uniform(Max), + N = rand:uniform(Max), L = [ rand_decomp() || _ <- lists:seq(1,N) ], LC = [ A || {A,_} <- L], LD = lists:flatten([B || {_,B} <- L]), LB = unicode:characters_to_binary(LD,unicode,utf8), {LC,LB}. - + rand_decomp() -> BT = bigtup(), SZ = tuple_size(BT), - element(random:uniform(SZ),BT). + element(rand:uniform(SZ),BT). bigtup() -> {{192,[65,768]}, {200,[69,768]}, diff --git a/lib/kernel/test/gen_sctp_SUITE.erl b/lib/kernel/test/gen_sctp_SUITE.erl index 881aaed429..a0ae792ba9 100644 --- a/lib/kernel/test/gen_sctp_SUITE.erl +++ b/lib/kernel/test/gen_sctp_SUITE.erl @@ -1,24 +1,25 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2013. All Rights Reserved. +%% Copyright Ericsson AB 2007-2018. 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/. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at %% -%% 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. +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% -module(gen_sctp_SUITE). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -include_lib("kernel/include/inet_sctp.hrl"). %%-compile(export_all). @@ -28,7 +29,8 @@ init_per_group/2,end_per_group/2, init_per_testcase/2, end_per_testcase/2]). -export( - [basic/1, + [skip_old_solaris/1, + basic/1, api_open_close/1,api_listen/1,api_connect_init/1,api_opts/1, xfer_min/1,xfer_active/1,def_sndrcvinfo/1,implicit_inet6/1, open_multihoming_ipv4_socket/1, @@ -41,22 +43,32 @@ names_unihoming_ipv4/1, names_unihoming_ipv6/1, names_multihoming_ipv4/1, names_multihoming_ipv6/1]). -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap,{minutes,1}}]. -all() -> - [basic, api_open_close, api_listen, api_connect_init, - api_opts, xfer_min, xfer_active, def_sndrcvinfo, implicit_inet6, - open_multihoming_ipv4_socket, - open_unihoming_ipv6_socket, - open_multihoming_ipv6_socket, - open_multihoming_ipv4_and_ipv6_socket, active_n, - basic_stream, xfer_stream_min, peeloff_active_once, - peeloff_active_true, peeloff_active_n, buffers, - names_unihoming_ipv4, names_unihoming_ipv6, - names_multihoming_ipv4, names_multihoming_ipv6]. +all() -> + G = case is_old_solaris() of + true -> old_solaris; + false -> extensive + end, + [{group,smoke}, + {group,G}]. groups() -> - []. + [{smoke,[],[basic,basic_stream]}, + {old_solaris,[],[skip_old_solaris]}, + {extensive,[], + [api_open_close, api_listen, api_connect_init, + api_opts, xfer_min, xfer_active, def_sndrcvinfo, implicit_inet6, + open_multihoming_ipv4_socket, + open_unihoming_ipv6_socket, + open_multihoming_ipv6_socket, + open_multihoming_ipv4_and_ipv6_socket, active_n, + xfer_stream_min, peeloff_active_once, + peeloff_active_true, peeloff_active_n, buffers, + names_unihoming_ipv4, names_unihoming_ipv6, + names_multihoming_ipv4, names_multihoming_ipv6]}]. init_per_suite(_Config) -> case gen_sctp:open() of @@ -80,48 +92,48 @@ end_per_group(_GroupName, Config) -> init_per_testcase(_Func, Config) -> - Dog = test_server:timetrap(test_server:seconds(15)), - [{watchdog, Dog}|Config]. -end_per_testcase(_Func, Config) -> - Dog = ?config(watchdog, Config), - test_server:timetrap_cancel(Dog). + Config. +end_per_testcase(_Func, _Config) -> + ok. -define(LOGVAR(Var), begin io:format(??Var" = ~p~n", [Var]) end). +is_old_solaris() -> + os:type() =:= {unix,sunos} andalso os:version() < {5,12,0}. +skip_old_solaris(_Config) -> + {skip,"Unreliable test cases and/or implementation on old Solaris"}. -basic(doc) -> - "Hello world"; -basic(suite) -> - []; +%% Hello world. basic(Config) when is_list(Config) -> - ?line {ok,S} = gen_sctp:open(), - ?line ok = gen_sctp:close(S), + {ok,S} = gen_sctp:open(), + ok = gen_sctp:close(S), ok. -xfer_min(doc) -> - "Minimal data transfer"; -xfer_min(suite) -> - []; +%% Minimal data transfer. xfer_min(Config) when is_list(Config) -> - ?line Stream = 0, - ?line Data = <<"The quick brown fox jumps over a lazy dog 0123456789">>, - ?line Loopback = {127,0,0,1}, - ?line {ok,Sb} = gen_sctp:open([{type,seqpacket}]), - ?line {ok,Pb} = inet:port(Sb), - ?line ok = gen_sctp:listen(Sb, true), - - ?line {ok,Sa} = gen_sctp:open(), - ?line {ok,Pa} = inet:port(Sa), - ?line {ok,#sctp_assoc_change{state=comm_up, - error=0, - outbound_streams=SaOutboundStreams, - inbound_streams=SaInboundStreams, - assoc_id=SaAssocId}=SaAssocChange} = + Stream = 0, + Data = <<"The quick brown fox jumps over a lazy dog 0123456789">>, + Loopback = {127,0,0,1}, + StatOpts = + [recv_avg,recv_cnt,recv_max,recv_oct, + send_avg,send_cnt,send_max,send_oct], + {ok,Sb} = gen_sctp:open([{type,seqpacket}]), + {ok,SbStat1} = inet:getstat(Sb, StatOpts), + {ok,Pb} = inet:port(Sb), + ok = gen_sctp:listen(Sb, true), + + {ok,Sa} = gen_sctp:open(), + {ok,Pa} = inet:port(Sa), + {ok,#sctp_assoc_change{state=comm_up, + error=0, + outbound_streams=SaOutboundStreams, + inbound_streams=SaInboundStreams, + assoc_id=SaAssocId}=SaAssocChange} = gen_sctp:connect(Sa, Loopback, Pb, []), - ?line {SbAssocId,SaOutboundStreams,SaInboundStreams} = + {SbAssocId,SaOutboundStreams,SaInboundStreams} = case recv_event(log_ok(gen_sctp:recv(Sb, infinity))) of {Loopback,Pa, #sctp_assoc_change{state=comm_up, @@ -141,184 +153,195 @@ xfer_min(Config) when is_list(Config) -> outbound_streams=SbOutboundStreams, inbound_streams=SbInboundStreams, assoc_id=AssocId}} = - ?line recv_event(log_ok(gen_sctp:recv(Sb, infinity))), + recv_event(log_ok(gen_sctp:recv(Sb, infinity))), {AssocId,SbInboundStreams,SbOutboundStreams} end, - ?line ok = gen_sctp:send(Sa, SaAssocId, 0, Data), - ?line case log_ok(gen_sctp:recv(Sb, infinity)) of - {Loopback, - Pa, - [#sctp_sndrcvinfo{stream=Stream, - assoc_id=SbAssocId}], - Data} -> ok; - Event1 -> - case recv_event(Event1) of - {Loopback,Pa, - #sctp_paddr_change{addr = {Loopback,_}, - state = State, - error = 0, - assoc_id = SbAssocId}} - when State =:= addr_available; - State =:= addr_confirmed -> - {Loopback, - Pa, - [#sctp_sndrcvinfo{stream=Stream, - assoc_id=SbAssocId}], - Data} = log_ok(gen_sctp:recv(Sb, infinity)) - end - end, - ?line ok = gen_sctp:send(Sb, SbAssocId, 0, Data), - ?line case log_ok(gen_sctp:recv(Sa, infinity)) of - {Loopback,Pb, - [#sctp_sndrcvinfo{stream=Stream, - assoc_id=SaAssocId}], - Data} -> - ok; - Event2 -> - {Loopback,Pb, - #sctp_paddr_change{addr={_,Pb}, - state=addr_confirmed, - error=0, - assoc_id=SaAssocId}} = - ?line recv_event(Event2), - ?line {Loopback, - Pb, - [#sctp_sndrcvinfo{stream=Stream, - assoc_id=SaAssocId}], - Data} = - log_ok(gen_sctp:recv(Sa, infinity)) - end, + ok = gen_sctp:send(Sa, SaAssocId, 0, Data), + case log_ok(gen_sctp:recv(Sb, infinity)) of + {Loopback, + Pa, + [#sctp_sndrcvinfo{stream=Stream, + assoc_id=SbAssocId}], + Data} -> ok; + Event1 -> + case recv_event(Event1) of + {Loopback,Pa, + #sctp_paddr_change{addr = {Loopback,_}, + state = State, + error = 0, + assoc_id = SbAssocId}} + when State =:= addr_available; + State =:= addr_confirmed -> + {Loopback, + Pa, + [#sctp_sndrcvinfo{stream=Stream, + assoc_id=SbAssocId}], + Data} = log_ok(gen_sctp:recv(Sb, infinity)) + end + end, + ok = gen_sctp:send(Sb, SbAssocId, 0, Data), + case log_ok(gen_sctp:recv(Sa, infinity)) of + {Loopback,Pb, + [#sctp_sndrcvinfo{stream=Stream, + assoc_id=SaAssocId}], + Data} -> + ok; + Event2 -> + {Loopback,Pb, + #sctp_paddr_change{addr={_,Pb}, + state=addr_confirmed, + error=0, + assoc_id=SaAssocId}} = + recv_event(Event2), + {Loopback, + Pb, + [#sctp_sndrcvinfo{stream=Stream, + assoc_id=SaAssocId}], + Data} = + log_ok(gen_sctp:recv(Sa, infinity)) + end, %% - ?line ok = gen_sctp:eof(Sa, SaAssocChange), - ?line {Loopback,Pa,#sctp_shutdown_event{assoc_id=SbAssocId}} = + ok = gen_sctp:eof(Sa, SaAssocChange), + {Loopback,Pa,#sctp_shutdown_event{assoc_id=SbAssocId}} = recv_event(log_ok(gen_sctp:recv(Sb, infinity))), - ?line {Loopback,Pb, - #sctp_assoc_change{state=shutdown_comp, - error=0, - assoc_id=SaAssocId}} = + {Loopback,Pb, + #sctp_assoc_change{state=shutdown_comp, + error=0, + assoc_id=SaAssocId}} = recv_event(log_ok(gen_sctp:recv(Sa, infinity))), - ?line {Loopback,Pa, - #sctp_assoc_change{state=shutdown_comp, - error=0, - assoc_id=SbAssocId}} = + {Loopback,Pa, + #sctp_assoc_change{state=shutdown_comp, + error=0, + assoc_id=SbAssocId}} = recv_event(log_ok(gen_sctp:recv(Sb, infinity))), - ?line ok = gen_sctp:close(Sa), - ?line ok = gen_sctp:close(Sb), + ok = gen_sctp:close(Sa), + {ok,SbStat2} = inet:getstat(Sb, StatOpts), + [] = filter_stat_eq(SbStat1, SbStat2), + ok = gen_sctp:close(Sb), - ?line receive - Msg -> test_server:fail({received,Msg}) - after 17 -> ok - end, + receive + Msg -> ct:fail({received,Msg}) + after 17 -> ok + end, ok. -xfer_active(doc) -> - "Minimal data transfer in active mode"; -xfer_active(suite) -> +filter_stat_eq([], []) -> []; +filter_stat_eq([{Tag,Val1}=Stat|SbStat1], [{Tag,Val2}|SbStat2]) -> + if + Val1 == Val2 -> + [Stat|filter_stat_eq(SbStat1, SbStat2)]; + true -> + filter_stat_eq(SbStat1, SbStat2) + end. + + + +%% Minimal data transfer in active mode. xfer_active(Config) when is_list(Config) -> - ?line Timeout = 2000, - ?line Stream = 0, - ?line Data = <<"The quick brown fox jumps over a lazy dog 0123456789">>, - ?line Loopback = {127,0,0,1}, - ?line {ok,Sb} = gen_sctp:open([{active,true}]), - ?line {ok,Pb} = inet:port(Sb), - ?line ok = gen_sctp:listen(Sb, true), - - ?line {ok,Sa} = gen_sctp:open([{active,true}]), - ?line {ok,Pa} = inet:port(Sa), - ?line ok = gen_sctp:connect_init(Sa, Loopback, Pb, []), - ?line #sctp_assoc_change{state=comm_up, - error=0, - outbound_streams=SaOutboundStreams, - inbound_streams=SaInboundStreams, - assoc_id=SaAssocId} = SaAssocChange = + Timeout = 2000, + Stream = 0, + Data = <<"The quick brown fox jumps over a lazy dog 0123456789">>, + Loopback = {127,0,0,1}, + {ok,Sb} = gen_sctp:open([{active,true}]), + {ok,Pb} = inet:port(Sb), + ok = gen_sctp:listen(Sb, true), + + {ok,Sa} = gen_sctp:open([{active,true}]), + {ok,Pa} = inet:port(Sa), + ok = gen_sctp:connect_init(Sa, Loopback, Pb, []), + #sctp_assoc_change{state=comm_up, + error=0, + outbound_streams=SaOutboundStreams, + inbound_streams=SaInboundStreams, + assoc_id=SaAssocId} = SaAssocChange = recv_assoc_change(Sa, Loopback, Pb, Timeout), - ?line io:format("Sa=~p, Pa=~p, Sb=~p, Pb=~p, SaAssocId=~p, " - "SaOutboundStreams=~p, SaInboundStreams=~p~n", - [Sa,Pa,Sb,Pb,SaAssocId, - SaOutboundStreams,SaInboundStreams]), - ?line #sctp_assoc_change{state=comm_up, - error=0, - outbound_streams=SbOutboundStreams, - inbound_streams=SbInboundStreams, - assoc_id=SbAssocId} = + io:format("Sa=~p, Pa=~p, Sb=~p, Pb=~p, SaAssocId=~p, " + "SaOutboundStreams=~p, SaInboundStreams=~p~n", + [Sa,Pa,Sb,Pb,SaAssocId, + SaOutboundStreams,SaInboundStreams]), + #sctp_assoc_change{state=comm_up, + error=0, + outbound_streams=SbOutboundStreams, + inbound_streams=SbInboundStreams, + assoc_id=SbAssocId} = recv_assoc_change(Sb, Loopback, Pa, Timeout), - ?line SbOutboundStreams = SaInboundStreams, - ?line SbInboundStreams = SaOutboundStreams, - ?line io:format("SbAssocId=~p~n", [SbAssocId]), - - ?line case recv_paddr_change(Sa, Loopback, Pb, 314) of - #sctp_paddr_change{state=addr_confirmed, - addr={_,Pb}, - error=0, - assoc_id=SaAssocId} -> ok; - #sctp_paddr_change{state=addr_available, - addr={_,Pb}, - error=0, - assoc_id=SaAssocId} -> ok; - timeout -> ok - end, - ?line case recv_paddr_change(Sb, Loopback, Pa, 314) of - #sctp_paddr_change{state=addr_confirmed, - addr={Loopback,Pa}, - error=0, - assoc_id=SbAssocId} -> ok; - #sctp_paddr_change{state=addr_available, - addr={Loopback,P}, - error=0, - assoc_id=SbAssocId} -> - ?line match_unless_solaris(Pa, P); - timeout -> ok - end, - ?line [] = flush(), - - ?line ok = + SbOutboundStreams = SaInboundStreams, + SbInboundStreams = SaOutboundStreams, + io:format("SbAssocId=~p~n", [SbAssocId]), + + case recv_paddr_change(Sa, Loopback, Pb, 314) of + #sctp_paddr_change{state=addr_confirmed, + addr={_,Pb}, + error=0, + assoc_id=SaAssocId} -> ok; + #sctp_paddr_change{state=addr_available, + addr={_,Pb}, + error=0, + assoc_id=SaAssocId} -> ok; + timeout -> ok + end, + case recv_paddr_change(Sb, Loopback, Pa, 314) of + #sctp_paddr_change{state=addr_confirmed, + addr={Loopback,Pa}, + error=0, + assoc_id=SbAssocId} -> ok; + #sctp_paddr_change{state=addr_available, + addr={Loopback,P}, + error=0, + assoc_id=SbAssocId} -> + match_unless_solaris(Pa, P); + timeout -> ok + end, + [] = flush(), + + ok = do_from_other_process( fun () -> gen_sctp:send(Sa, SaAssocId, 0, Data) end), - ?line receive - {sctp,Sb,Loopback,Pa, - {[#sctp_sndrcvinfo{stream=Stream, - assoc_id=SbAssocId}], - Data}} -> ok - after Timeout -> - ?line test_server:fail({timeout,flush()}) - end, - ?line ok = gen_sctp:send(Sb, SbAssocId, 0, Data), - ?line receive - {sctp,Sa,Loopback,Pb, - {[#sctp_sndrcvinfo{stream=Stream, - assoc_id=SaAssocId}], - Data}} -> ok - after Timeout -> - ?line test_server:fail({timeout,flush()}) - end, + receive + {sctp,Sb,Loopback,Pa, + {[#sctp_sndrcvinfo{stream=Stream, + assoc_id=SbAssocId}], + Data}} -> ok + after Timeout -> + ct:fail({timeout,flush()}) + end, + ok = gen_sctp:send(Sb, SbAssocId, 0, Data), + receive + {sctp,Sa,Loopback,Pb, + {[#sctp_sndrcvinfo{stream=Stream, + assoc_id=SaAssocId}], + Data}} -> ok + after Timeout -> + ct:fail({timeout,flush()}) + end, %% - ?line ok = gen_sctp:abort(Sa, SaAssocChange), - ?line case recv_assoc_change(Sb, Loopback, Pa, Timeout) of - #sctp_assoc_change{state=comm_lost, - assoc_id=SbAssocId} -> ok; - timeout -> - ?line test_server:fail({timeout,flush()}) - end, - ?line ok = gen_sctp:close(Sb), - ?line case recv_assoc_change(Sa, Loopback, Pb, Timeout) of - #sctp_assoc_change{state=comm_lost, - assoc_id=SaAssocId} -> ok; - timeout -> - ?line io:format("timeout waiting for comm_lost on Sa~n"), - ?line match_unless_solaris(ok, {timeout,flush()}) - end, - ?line receive - {sctp_error,Sa,enotconn} -> ok % Solaris - after 17 -> ok - end, - ?line ok = gen_sctp:close(Sa), + ok = gen_sctp:abort(Sa, SaAssocChange), + case recv_assoc_change(Sb, Loopback, Pa, Timeout) of + #sctp_assoc_change{state=comm_lost, + assoc_id=SbAssocId} -> ok; + timeout -> + ct:fail({timeout,flush()}) + end, + ok = gen_sctp:close(Sb), + case recv_assoc_change(Sa, Loopback, Pb, Timeout) of + #sctp_assoc_change{state=comm_lost, + assoc_id=SaAssocId} -> ok; + timeout -> + io:format("timeout waiting for comm_lost on Sa~n"), + match_unless_solaris(ok, {timeout,flush()}) + end, + receive + {sctp_error,Sa,enotconn} -> ok % Solaris + after 17 -> ok + end, + ok = gen_sctp:close(Sa), %% - ?line receive - Msg -> test_server:fail({unexpected,[Msg]++flush()}) - after 17 -> ok - end, + receive + Msg -> ct:fail({unexpected,[Msg]++flush()}) + after 17 -> ok + end, ok. recv_assoc_change(S, Addr, Port, Timeout) -> @@ -345,142 +368,141 @@ recv_paddr_change(S, Addr, Port, Timeout) -> timeout end. -def_sndrcvinfo(doc) -> - "Test that #sctp_sndrcvinfo{} parameters set on a socket " - "are used by gen_sctp:send/4"; -def_sndrcvinfo(suite) -> - []; +%% Test that #sctp_sndrcvinfo{} parameters set on a socket +%% are used by gen_sctp:send/4. def_sndrcvinfo(Config) when is_list(Config) -> - ?line Loopback = {127,0,0,1}, - ?line Data = <<"What goes up, must come down.">>, + Loopback = {127,0,0,1}, + Data = <<"What goes up, must come down.">>, %% - ?line S1 = + S1 = log_ok(gen_sctp:open( 0, [{sctp_default_send_param,#sctp_sndrcvinfo{ppid=17}}])), ?LOGVAR(S1), - ?line P1 = + P1 = log_ok(inet:port(S1)), ?LOGVAR(P1), - ?line #sctp_sndrcvinfo{ppid=17, context=0, timetolive=0, assoc_id=0} = + #sctp_sndrcvinfo{ppid=17, context=0, timetolive=0, assoc_id=0} = getopt(S1, sctp_default_send_param), - ?line ok = + ok = gen_sctp:listen(S1, true), %% - ?line S2 = + S2 = log_ok(gen_sctp:open()), ?LOGVAR(S2), - ?line P2 = + P2 = log_ok(inet:port(S2)), ?LOGVAR(P2), - ?line #sctp_sndrcvinfo{ppid=0, context=0, timetolive=0, assoc_id=0} = + #sctp_sndrcvinfo{ppid=0, context=0, timetolive=0, assoc_id=0} = getopt(S2, sctp_default_send_param), %% - ?line #sctp_assoc_change{ + #sctp_assoc_change{ state=comm_up, error=0, assoc_id=S2AssocId} = S2AssocChange = log_ok(gen_sctp:connect(S2, Loopback, P1, [])), ?LOGVAR(S2AssocChange), - ?line case recv_event(log_ok(gen_sctp:recv(S1))) of - {Loopback,P2, - #sctp_assoc_change{ - state=comm_up, - error=0, - assoc_id=S1AssocId}} -> - ?LOGVAR(S1AssocId); - {Loopback,P2, - #sctp_paddr_change{ - state=addr_confirmed, - error=0, - assoc_id=S1AssocId}} -> - ?LOGVAR(S1AssocId), - {Loopback,P2, - #sctp_assoc_change{ - state=comm_up, - error=0, - assoc_id=S1AssocId}} = - recv_event(log_ok(gen_sctp:recv(S1))) - end, - - ?line #sctp_sndrcvinfo{ + S1AssocId = + case recv_event(log_ok(gen_sctp:recv(S1))) of + {Loopback,P2, + #sctp_assoc_change{ + state=comm_up, + error=0, + assoc_id=AssocId}} -> + AssocId; + {Loopback,P2, + #sctp_paddr_change{ + state=addr_confirmed, + error=0, + assoc_id=AssocId}} -> + {Loopback,P2, + #sctp_assoc_change{ + state=comm_up, + error=0, + assoc_id=AssocId}} = + recv_event(log_ok(gen_sctp:recv(S1))), + AssocId + end, + ?LOGVAR(S1AssocId), + + #sctp_sndrcvinfo{ ppid=17, context=0, timetolive=0} = %, assoc_id=S1AssocId} = getopt( S1, sctp_default_send_param, #sctp_sndrcvinfo{assoc_id=S1AssocId}), - ?line #sctp_sndrcvinfo{ + #sctp_sndrcvinfo{ ppid=0, context=0, timetolive=0} = %, assoc_id=S2AssocId} = getopt( S2, sctp_default_send_param, #sctp_sndrcvinfo{assoc_id=S2AssocId}), %% - ?line ok = + ok = gen_sctp:send(S1, S1AssocId, 1, <<"1: ",Data/binary>>), - ?line case log_ok(gen_sctp:recv(S2)) of - {Loopback,P1, - [#sctp_sndrcvinfo{ - stream=1, ppid=17, context=0, assoc_id=S2AssocId}], - <<"1: ",Data/binary>>} -> ok; - Event1 -> - ?line {Loopback,P1, - #sctp_paddr_change{state=addr_confirmed, - addr={_,P1}, - error=0, - assoc_id=S2AssocId}} = - recv_event(Event1), - ?line {Loopback,P1, - [#sctp_sndrcvinfo{ - stream=1, ppid=17, context=0, assoc_id=S2AssocId}], - <<"1: ",Data/binary>>} = - log_ok(gen_sctp:recv(S2)) - end, + case log_ok(gen_sctp:recv(S2)) of + {Loopback,P1, + [#sctp_sndrcvinfo{ + stream=1, ppid=17, context=0, assoc_id=S2AssocId}], + <<"1: ",Data/binary>>} -> ok; + Event1 -> + {Loopback,P1, + #sctp_paddr_change{state=addr_confirmed, + addr={_,P1}, + error=0, + assoc_id=S2AssocId}} = + recv_event(Event1), + {Loopback,P1, + [#sctp_sndrcvinfo{ + stream=1, ppid=17, context=0, assoc_id=S2AssocId}], + <<"1: ",Data/binary>>} = + log_ok(gen_sctp:recv(S2)) + end, %% - ?line ok = + ok = setopt( S1, sctp_default_send_param, #sctp_sndrcvinfo{ppid=18}), - ?line ok = + ok = setopt( S1, sctp_default_send_param, #sctp_sndrcvinfo{ppid=19, assoc_id=S1AssocId}), - ?line #sctp_sndrcvinfo{ + #sctp_sndrcvinfo{ ppid=18, context=0, timetolive=0, assoc_id=0} = getopt(S1, sctp_default_send_param), - ?line #sctp_sndrcvinfo{ + #sctp_sndrcvinfo{ ppid=19, context=0, timetolive=0, assoc_id=S1AssocId} = getopt( S1, sctp_default_send_param, #sctp_sndrcvinfo{assoc_id=S1AssocId}), %% - ?line ok = + ok = gen_sctp:send(S1, S1AssocId, 0, <<"2: ",Data/binary>>), - ?line case log_ok(gen_sctp:recv(S2)) of - {Loopback,P1, - [#sctp_sndrcvinfo{ - stream=0, ppid=19, context=0, assoc_id=S2AssocId}], - <<"2: ",Data/binary>>} -> ok - end, - ?line ok = + case log_ok(gen_sctp:recv(S2)) of + {Loopback,P1, + [#sctp_sndrcvinfo{ + stream=0, ppid=19, context=0, assoc_id=S2AssocId}], + <<"2: ",Data/binary>>} -> ok + end, + ok = gen_sctp:send(S2, S2AssocChange, 1, <<"3: ",Data/binary>>), - ?line case log_ok(gen_sctp:recv(S1)) of - {Loopback,P2, - [#sctp_sndrcvinfo{ - stream=1, ppid=0, context=0, assoc_id=S1AssocId}], - <<"3: ",Data/binary>>} -> ok; - Event2 -> - case recv_event(Event2) of - {Loopback,P2, - #sctp_paddr_change{ - addr={Loopback,_}, - state=State, - error=0, assoc_id=S1AssocId}} - when State =:= addr_available; - State =:= addr_confirmed -> - ?line case log_ok(gen_sctp:recv(S1)) of - {Loopback,P2, - [#sctp_sndrcvinfo{ - stream=1, ppid=0, context=0, - assoc_id=S1AssocId}], - <<"3: ",Data/binary>>} -> ok - end - end - end, - ?line ok = + case log_ok(gen_sctp:recv(S1)) of + {Loopback,P2, + [#sctp_sndrcvinfo{ + stream=1, ppid=0, context=0, assoc_id=S1AssocId}], + <<"3: ",Data/binary>>} -> ok; + Event2 -> + case recv_event(Event2) of + {Loopback,P2, + #sctp_paddr_change{ + addr={Loopback,_}, + state=State, + error=0, assoc_id=S1AssocId}} + when State =:= addr_available; + State =:= addr_confirmed -> + case log_ok(gen_sctp:recv(S1)) of + {Loopback,P2, + [#sctp_sndrcvinfo{ + stream=1, ppid=0, context=0, + assoc_id=S1AssocId}], + <<"3: ",Data/binary>>} -> ok + end + end + end, + ok = do_from_other_process( fun () -> gen_sctp:send( @@ -488,22 +510,22 @@ def_sndrcvinfo(Config) when is_list(Config) -> #sctp_sndrcvinfo{stream=0, ppid=20, assoc_id=S2AssocId}, <<"4: ",Data/binary>>) end), - ?line case log_ok(do_from_other_process(fun() -> gen_sctp:recv(S1) end)) of - {Loopback,P2, - [#sctp_sndrcvinfo{ - stream=0, ppid=20, context=0, assoc_id=S1AssocId}], - <<"4: ",Data/binary>>} -> ok - end, + case log_ok(do_from_other_process(fun() -> gen_sctp:recv(S1) end)) of + {Loopback,P2, + [#sctp_sndrcvinfo{ + stream=0, ppid=20, context=0, assoc_id=S1AssocId}], + <<"4: ",Data/binary>>} -> ok + end, %% - ?line ok = + ok = gen_sctp:close(S1), - ?line ok = + ok = gen_sctp:close(S2), - ?line receive - Msg -> - test_server:fail({received,Msg}) - after 17 -> ok - end, + receive + Msg -> + ct:fail({received,Msg}) + after 17 -> ok + end, ok. getopt(S, Opt) -> @@ -540,147 +562,138 @@ flush() -> [] end. -api_open_close(doc) -> - "Test the API function open/1,2 and close/1"; -api_open_close(suite) -> - []; +%% Test the API function open/1,2 and close/1. api_open_close(Config) when is_list(Config) -> - ?line {ok,S1} = gen_sctp:open(0), - ?line {ok,P} = inet:port(S1), - ?line ok = gen_sctp:close(S1), + {ok,S1} = gen_sctp:open(0), + {ok,P} = inet:port(S1), + ok = gen_sctp:close(S1), - ?line {ok,S2} = gen_sctp:open(P), - ?line {ok,P} = inet:port(S2), - ?line ok = gen_sctp:close(S2), + {ok,S2} = gen_sctp:open(P), + {ok,P} = inet:port(S2), + ok = gen_sctp:close(S2), - ?line {ok,S3} = gen_sctp:open([{port,P}]), - ?line {ok,P} = inet:port(S3), - ?line ok = gen_sctp:close(S3), + {ok,S3} = gen_sctp:open([{port,P}]), + {ok,P} = inet:port(S3), + ok = gen_sctp:close(S3), - ?line {ok,S4} = gen_sctp:open(P, []), - ?line {ok,P} = inet:port(S4), - ?line ok = gen_sctp:close(S4), + {ok,S4} = gen_sctp:open(P, []), + {ok,P} = inet:port(S4), + ok = gen_sctp:close(S4), - ?line {ok,S5} = gen_sctp:open(P, [{ifaddr,any}]), - ?line {ok,P} = inet:port(S5), - ?line ok = gen_sctp:close(S5), + {ok,S5} = gen_sctp:open(P, [{ifaddr,any}]), + {ok,P} = inet:port(S5), + ok = gen_sctp:close(S5), - ?line ok = gen_sctp:close(S5), + ok = gen_sctp:close(S5), - ?line try gen_sctp:close(0) - catch error:badarg -> ok - end, + try gen_sctp:close(0) + catch error:badarg -> ok + end, - ?line try gen_sctp:open({}) - catch error:badarg -> ok - end, + try gen_sctp:open({}) + catch error:badarg -> ok + end, - ?line try gen_sctp:open(-1) - catch error:badarg -> ok - end, + try gen_sctp:open(-1) + catch error:badarg -> ok + end, - ?line try gen_sctp:open(65536) - catch error:badarg -> ok - end, + try gen_sctp:open(65536) + catch error:badarg -> ok + end, - ?line try gen_sctp:open(make_ref(), []) - catch error:badarg -> ok - end, + try gen_sctp:open(make_ref(), []) + catch error:badarg -> ok + end, - ?line try gen_sctp:open(0, {}) - catch error:badarg -> ok - end, + try gen_sctp:open(0, {}) + catch error:badarg -> ok + end, - ?line try gen_sctp:open(0, [make_ref()]) - catch error:badarg -> ok - end, + try gen_sctp:open(0, [make_ref()]) + catch error:badarg -> ok + end, - ?line try gen_sctp:open([{invalid_option,0}]) - catch error:badarg -> ok - end, + try gen_sctp:open([{invalid_option,0}]) + catch error:badarg -> ok + end, - ?line try gen_sctp:open(0, [{mode,invalid_mode}]) - catch error:badarg -> ok - end, + try gen_sctp:open(0, [{mode,invalid_mode}]) + catch error:badarg -> ok + end, ok. -api_listen(doc) -> - "Test the API function listen/2"; -api_listen(suite) -> - []; +%% Test the API function listen/2. api_listen(Config) when is_list(Config) -> - ?line Localhost = {127,0,0,1}, - - ?line try gen_sctp:listen(0, true) - catch error:badarg -> ok - end, - - ?line {ok,S} = gen_sctp:open(), - ?line {ok,Pb} = inet:port(S), - ?line try gen_sctp:listen(S, not_allowed_for_listen) - catch error:badarg -> ok - end, - ?line ok = gen_sctp:close(S), - ?line {error,closed} = gen_sctp:listen(S, true), - - ?line {ok,Sb} = gen_sctp:open(Pb), - ?line {ok,Sa} = gen_sctp:open(), - ?line case gen_sctp:connect(Sa, localhost, Pb, []) of - {error,econnrefused} -> - ?line {ok,{Localhost, - Pb,[], - #sctp_assoc_change{ - state=comm_lost}}} = - gen_sctp:recv(Sa, infinity); - {error,#sctp_assoc_change{state=cant_assoc}} -> - ok%; - %% {error,{Localhost,Pb,_,#sctp_assoc_change{state=cant_assoc}}} -> - %% ok - end, - ?line ok = gen_sctp:listen(Sb, true), - ?line {ok,#sctp_assoc_change{state=comm_up, - error=0}} = + Localhost = {127,0,0,1}, + + try gen_sctp:listen(0, true) + catch error:badarg -> ok + end, + + {ok,S} = gen_sctp:open(), + {ok,Pb} = inet:port(S), + try gen_sctp:listen(S, not_allowed_for_listen) + catch error:badarg -> ok + end, + ok = gen_sctp:close(S), + {error,closed} = gen_sctp:listen(S, true), + + {ok,Sb} = gen_sctp:open(Pb), + {ok,Sa} = gen_sctp:open(), + case gen_sctp:connect(Sa, localhost, Pb, []) of + {error,econnrefused} -> + {ok,{Localhost, + Pb,[], + #sctp_assoc_change{ + state=comm_lost}}} = + gen_sctp:recv(Sa, infinity); + {error,#sctp_assoc_change{state=cant_assoc}} -> + ok%; + %% {error,{Localhost,Pb,_,#sctp_assoc_change{state=cant_assoc}}} -> + %% ok + end, + ok = gen_sctp:listen(Sb, true), + {ok,#sctp_assoc_change{state=comm_up, + error=0}} = gen_sctp:connect(Sa, localhost, Pb, []), - ?line ok = gen_sctp:close(Sa), - ?line ok = gen_sctp:close(Sb), + ok = gen_sctp:close(Sa), + ok = gen_sctp:close(Sb), ok. -api_connect_init(doc) -> - "Test the API function connect_init/4"; -api_connect_init(suite) -> - []; +%% Test the API function connect_init/4. api_connect_init(Config) when is_list(Config) -> - ?line Localhost = {127,0,0,1}, - - ?line {ok,S} = gen_sctp:open(), - ?line {ok,Pb} = inet:port(S), - ?line try gen_sctp:connect_init(S, Localhost, not_allowed_for_port, []) - catch error:badarg -> ok - end, - ?line try gen_sctp:connect_init(S, Localhost, 12345, not_allowed_for_opts) - catch error:badarg -> ok - end, - ?line ok = gen_sctp:close(S), - ?line {error,closed} = gen_sctp:connect_init(S, Localhost, 12345, []), - - ?line {ok,Sb} = gen_sctp:open(Pb), - ?line {ok,Sa} = gen_sctp:open(), - ?line case gen_sctp:connect_init(Sa, localhost, Pb, []) of - {error,econnrefused} -> - ?line {Localhost,Pb,#sctp_assoc_change{state=comm_lost}} = - recv_event(log_ok(gen_sctp:recv(Sa, infinity))); - ok -> - ?line {Localhost,Pb,#sctp_assoc_change{state=cant_assoc}} = - recv_event(log_ok(gen_sctp:recv(Sa, infinity))) - end, - ?line ok = gen_sctp:listen(Sb, true), - ?line case gen_sctp:connect_init(Sa, localhost, Pb, []) of - ok -> - ?line {Localhost,Pb,#sctp_assoc_change{state=comm_up}} = - recv_event(log_ok(gen_sctp:recv(Sa, infinity))) - end, - ?line ok = gen_sctp:close(Sa), - ?line ok = gen_sctp:close(Sb), + Localhost = {127,0,0,1}, + + {ok,S} = gen_sctp:open(), + {ok,Pb} = inet:port(S), + try gen_sctp:connect_init(S, Localhost, not_allowed_for_port, []) + catch error:badarg -> ok + end, + try gen_sctp:connect_init(S, Localhost, 12345, not_allowed_for_opts) + catch error:badarg -> ok + end, + ok = gen_sctp:close(S), + {error,closed} = gen_sctp:connect_init(S, Localhost, 12345, []), + + {ok,Sb} = gen_sctp:open(Pb), + {ok,Sa} = gen_sctp:open(), + case gen_sctp:connect_init(Sa, localhost, Pb, []) of + {error,econnrefused} -> + {Localhost,Pb,#sctp_assoc_change{state=comm_lost}} = + recv_event(log_ok(gen_sctp:recv(Sa, infinity))); + ok -> + {Localhost,Pb,#sctp_assoc_change{state=cant_assoc}} = + recv_event(log_ok(gen_sctp:recv(Sa, infinity))) + end, + ok = gen_sctp:listen(Sb, true), + case gen_sctp:connect_init(Sa, localhost, Pb, []) of + ok -> + {Localhost,Pb,#sctp_assoc_change{state=comm_up}} = + recv_event(log_ok(gen_sctp:recv(Sa, infinity))) + end, + ok = gen_sctp:close(Sa), + ok = gen_sctp:close(Sb), ok. recv_event({Addr,Port,[],#sctp_assoc_change{}=AssocChange}) -> @@ -702,94 +715,86 @@ recv_event({Addr,Port, #sctp_shutdown_event{assoc_id=Assoc}=ShutdownEvent}) -> {Addr,Port,ShutdownEvent}. -api_opts(doc) -> - "Test socket options"; -api_opts(suite) -> - []; +%% Test socket options. api_opts(Config) when is_list(Config) -> - ?line Sndbuf = 32768, - ?line Recbuf = 65536, - ?line {ok,S} = gen_sctp:open(0), - ?line OSType = os:type(), - ?line case {inet:setopts(S, [{linger,{true,2}}]),OSType} of - {ok,_} -> - ok; - {{error,einval},{unix,sunos}} -> - ok - end, - ?line ok = inet:setopts(S, [{sndbuf,Sndbuf}]), - ?line ok = inet:setopts(S, [{recbuf,Recbuf}]), - ?line case inet:getopts(S, [sndbuf]) of - {ok,[{sndbuf,SB}]} when SB >= Sndbuf -> ok - end, - ?line case inet:getopts(S, [recbuf]) of - {ok,[{recbuf,RB}]} when RB >= Recbuf -> ok - end. + Sndbuf = 32768, + Recbuf = 65536, + {ok,S} = gen_sctp:open(0), + OSType = os:type(), + case {inet:setopts(S, [{linger,{true,2}}]),OSType} of + {ok,_} -> + ok; + {{error,einval},{unix,sunos}} -> + ok + end, + ok = inet:setopts(S, [{sndbuf,Sndbuf}]), + ok = inet:setopts(S, [{recbuf,Recbuf}]), + case inet:getopts(S, [sndbuf]) of + {ok,[{sndbuf,SB}]} when SB >= Sndbuf -> ok + end, + case inet:getopts(S, [recbuf]) of + {ok,[{recbuf,RB}]} when RB >= Recbuf -> ok + end. implicit_inet6(Config) when is_list(Config) -> - ?line Hostname = log_ok(inet:gethostname()), - ?line - case gen_sctp:open(0, [inet6]) of - {ok,S1} -> - ?line - case inet:getaddr(Hostname, inet6) of - {ok,Host} -> - ?line Loopback = {0,0,0,0,0,0,0,1}, - ?line io:format("~s ~p~n", ["Loopback",Loopback]), - ?line implicit_inet6(S1, Loopback), - ?line ok = gen_sctp:close(S1), - %% - ?line Localhost = - log_ok(inet:getaddr("localhost", inet6)), - ?line io:format("~s ~p~n", ["localhost",Localhost]), - ?line S2 = - log_ok(gen_sctp:open(0, [{ip,Localhost}])), - ?line implicit_inet6(S2, Localhost), - ?line ok = gen_sctp:close(S2), - %% - ?line io:format("~s ~p~n", [Hostname,Host]), - ?line S3 = - log_ok(gen_sctp:open(0, [{ifaddr,Host}])), - ?line implicit_inet6(S3, Host), - ?line ok = gen_sctp:close(S1); - {error,eafnosupport} -> - ?line ok = gen_sctp:close(S1), - {skip,"Can not look up IPv6 address"} - end; - _ -> - {skip,"IPv6 not supported"} - end. + Hostname = log_ok(inet:gethostname()), + case gen_sctp:open(0, [inet6]) of + {ok,S1} -> + case inet:getaddr(Hostname, inet6) of + {ok,Host} -> + Loopback = {0,0,0,0,0,0,0,1}, + io:format("~s ~p~n", ["Loopback",Loopback]), + implicit_inet6(S1, Loopback), + ok = gen_sctp:close(S1), + %% + Localhost = + log_ok(inet:getaddr("localhost", inet6)), + io:format("~s ~p~n", ["localhost",Localhost]), + S2 = + log_ok(gen_sctp:open(0, [{ip,Localhost}])), + implicit_inet6(S2, Localhost), + ok = gen_sctp:close(S2), + %% + io:format("~s ~p~n", [Hostname,Host]), + S3 = + log_ok(gen_sctp:open(0, [{ifaddr,Host}])), + implicit_inet6(S3, Host), + ok = gen_sctp:close(S1); + {error,eafnosupport} -> + ok = gen_sctp:close(S1), + {skip,"Can not look up IPv6 address"} + end; + _ -> + {skip,"IPv6 not supported"} + end. implicit_inet6(S1, Addr) -> - ?line ok = gen_sctp:listen(S1, true), - ?line P1 = log_ok(inet:port(S1)), - ?line S2 = log_ok(gen_sctp:open(0, [inet6])), - ?line P2 = log_ok(inet:port(S2)), - ?line #sctp_assoc_change{state=comm_up} = + ok = gen_sctp:listen(S1, true), + P1 = log_ok(inet:port(S1)), + S2 = log_ok(gen_sctp:open(0, [inet6])), + P2 = log_ok(inet:port(S2)), + #sctp_assoc_change{state=comm_up} = log_ok(gen_sctp:connect(S2, Addr, P1, [])), - ?line case recv_event(log_ok(gen_sctp:recv(S1))) of - {Addr,P2,#sctp_assoc_change{state=comm_up}} -> - ok; - {Addr,P2,#sctp_paddr_change{state=addr_confirmed, - addr={Addr,P2}, - error=0}} -> - {Addr,P2,#sctp_assoc_change{state=comm_up}} = - recv_event(log_ok(gen_sctp:recv(S1))) - end, - ?line case log_ok(inet:sockname(S1)) of - {Addr,P1} -> ok; - {{0,0,0,0,0,0,0,0},P1} -> ok - end, - ?line case log_ok(inet:sockname(S2)) of - {Addr,P2} -> ok; - {{0,0,0,0,0,0,0,0},P2} -> ok - end, - ?line ok = gen_sctp:close(S2). - -active_n(doc) -> - "Verify {active,N} socket management"; -active_n(suite) -> - []; + case recv_event(log_ok(gen_sctp:recv(S1))) of + {Addr,P2,#sctp_assoc_change{state=comm_up}} -> + ok; + {Addr,P2,#sctp_paddr_change{state=addr_confirmed, + addr={Addr,P2}, + error=0}} -> + {Addr,P2,#sctp_assoc_change{state=comm_up}} = + recv_event(log_ok(gen_sctp:recv(S1))) + end, + case log_ok(inet:sockname(S1)) of + {Addr,P1} -> ok; + {{0,0,0,0,0,0,0,0},P1} -> ok + end, + case log_ok(inet:sockname(S2)) of + {Addr,P2} -> ok; + {{0,0,0,0,0,0,0,0},P2} -> ok + end, + ok = gen_sctp:close(S2). + +%% Verify {active,N} socket management. active_n(Config) when is_list(Config) -> N = 3, S1 = ok(gen_sctp:open([{active,N}])), @@ -886,51 +891,45 @@ active_n(Config) when is_list(Config) -> ok = gen_sctp:close(S1), ok. -basic_stream(doc) -> - "Hello world stream socket"; -basic_stream(suite) -> - []; +%% Hello world stream socket. basic_stream(Config) when is_list(Config) -> - ?line {ok,S} = gen_sctp:open([{type,stream}]), - ?line ok = gen_sctp:listen(S, true), - ?line ok = + {ok,S} = gen_sctp:open([{type,stream}]), + ok = gen_sctp:listen(S, true), + ok = do_from_other_process( fun () -> gen_sctp:listen(S, 10) end), - ?line ok = gen_sctp:close(S), + ok = gen_sctp:close(S), ok. -xfer_stream_min(doc) -> - "Minimal data transfer"; -xfer_stream_min(suite) -> - []; +%% Minimal data transfer. xfer_stream_min(Config) when is_list(Config) -> - ?line Stream = 0, - ?line Data = <<"The quick brown fox jumps over a lazy dog 0123456789">>, - ?line Loopback = {127,0,0,1}, - ?line {ok,Sb} = gen_sctp:open([{type,seqpacket}]), - ?line ?LOGVAR(Sb), - ?line {ok,Pb} = inet:port(Sb), - ?line ?LOGVAR(Pb), - ?line ok = gen_sctp:listen(Sb, true), - - ?line {ok,Sa} = gen_sctp:open([{type,stream}]), - ?line ?LOGVAR(Sa), - ?line {ok,Pa} = inet:port(Sa), - ?line ?LOGVAR(Pa), - ?line #sctp_assoc_change{state=comm_up, - error=0, - outbound_streams=SaOutboundStreams, - inbound_streams=SaInboundStreams, - assoc_id=SaAssocId_X} = + Stream = 0, + Data = <<"The quick brown fox jumps over a lazy dog 0123456789">>, + Loopback = {127,0,0,1}, + {ok,Sb} = gen_sctp:open([{type,seqpacket}]), + ?LOGVAR(Sb), + {ok,Pb} = inet:port(Sb), + ?LOGVAR(Pb), + ok = gen_sctp:listen(Sb, true), + + {ok,Sa} = gen_sctp:open([{type,stream}]), + ?LOGVAR(Sa), + {ok,Pa} = inet:port(Sa), + ?LOGVAR(Pa), + #sctp_assoc_change{state=comm_up, + error=0, + outbound_streams=SaOutboundStreams, + inbound_streams=SaInboundStreams, + assoc_id=SaAssocId_X} = log_ok(gen_sctp:connect(Sa, Loopback, Pb, [])), - ?line ?LOGVAR(SaAssocId_X), - ?line [{_,#sctp_paddrinfo{assoc_id=SaAssocId,state=active}}] = + ?LOGVAR(SaAssocId_X), + [{_,#sctp_paddrinfo{assoc_id=SaAssocId,state=active}}] = log_ok(inet:getopts(Sa, [{sctp_get_peer_addr_info, #sctp_paddrinfo{address={Loopback,Pb}}}])), - ?line ?LOGVAR(SaAssocId), - ?line match_unless_solaris(SaAssocId_X, SaAssocId), + ?LOGVAR(SaAssocId), + match_unless_solaris(SaAssocId_X, SaAssocId), - ?line {SbOutboundStreams,SbInboundStreams,SbAssocId} = + {SbOutboundStreams,SbInboundStreams,SbAssocId} = case recv_event(log_ok(gen_sctp:recv(Sb, infinity))) of {Loopback,Pa, #sctp_assoc_change{state=comm_up, @@ -945,87 +944,87 @@ xfer_stream_min(Config) when is_list(Config) -> error=0, assoc_id=AI}} -> {Loopback,Pa, - ?line #sctp_assoc_change{state=comm_up, - error=0, - outbound_streams=OS, - inbound_streams=IS, - assoc_id=AI}} = + #sctp_assoc_change{state=comm_up, + error=0, + outbound_streams=OS, + inbound_streams=IS, + assoc_id=AI}} = recv_event(log_ok(gen_sctp:recv(Sb, infinity))), {OS,IS,AI} end, - ?line ?LOGVAR(SbAssocId), - ?line SaOutboundStreams = SbInboundStreams, - ?line ?LOGVAR(SaOutboundStreams), - ?line SbOutboundStreams = SaInboundStreams, - ?line ?LOGVAR(SbOutboundStreams), - ?line ok = gen_sctp:send(Sa, SaAssocId, 0, Data), - ?line case log_ok(gen_sctp:recv(Sb, infinity)) of - {Loopback, - Pa, - [#sctp_sndrcvinfo{stream=Stream, - assoc_id=SbAssocId}], - Data} -> ok; - {Loopback, - Pa,[], - #sctp_paddr_change{addr = {Loopback,_}, - state = addr_available, - error = 0, - assoc_id = SbAssocId}} -> - {Loopback, - Pa, - [#sctp_sndrcvinfo{stream=Stream, - assoc_id=SbAssocId}], - Data} = log_ok(gen_sctp:recv(Sb, infinity)); - {Loopback, - Pa, - [#sctp_sndrcvinfo{stream=Stream, - assoc_id=SbAssocId}], - #sctp_paddr_change{addr = {Loopback,_}, - state = addr_confirmed, - error = 0, - assoc_id = SbAssocId}} -> - {Loopback, - Pa, - [#sctp_sndrcvinfo{stream=Stream, - assoc_id=SbAssocId}], - Data} = log_ok(gen_sctp:recv(Sb, infinity)) - end, - ?line ok = + ?LOGVAR(SbAssocId), + SaOutboundStreams = SbInboundStreams, + ?LOGVAR(SaOutboundStreams), + SbOutboundStreams = SaInboundStreams, + ?LOGVAR(SbOutboundStreams), + ok = gen_sctp:send(Sa, SaAssocId, 0, Data), + case log_ok(gen_sctp:recv(Sb, infinity)) of + {Loopback, + Pa, + [#sctp_sndrcvinfo{stream=Stream, + assoc_id=SbAssocId}], + Data} -> ok; + {Loopback, + Pa,[], + #sctp_paddr_change{addr = {Loopback,_}, + state = addr_available, + error = 0, + assoc_id = SbAssocId}} -> + {Loopback, + Pa, + [#sctp_sndrcvinfo{stream=Stream, + assoc_id=SbAssocId}], + Data} = log_ok(gen_sctp:recv(Sb, infinity)); + {Loopback, + Pa, + [#sctp_sndrcvinfo{stream=Stream, + assoc_id=SbAssocId}], + #sctp_paddr_change{addr = {Loopback,_}, + state = addr_confirmed, + error = 0, + assoc_id = SbAssocId}} -> + {Loopback, + Pa, + [#sctp_sndrcvinfo{stream=Stream, + assoc_id=SbAssocId}], + Data} = log_ok(gen_sctp:recv(Sb, infinity)) + end, + ok = do_from_other_process( fun () -> gen_sctp:send(Sb, SbAssocId, 0, Data) end), - ?line case log_ok(gen_sctp:recv(Sa, infinity)) of - {Loopback,Pb, - [#sctp_sndrcvinfo{stream=Stream, - assoc_id=SaAssocId}], - Data} -> ok; - Event1 -> - ?line {Loopback,Pb, - #sctp_paddr_change{state=addr_confirmed, - addr={_,Pb}, - error=0, - assoc_id=SaAssocId}} = - recv_event(Event1), - ?line {Loopback,Pb, - [#sctp_sndrcvinfo{stream=Stream, - assoc_id=SaAssocId}], - Data} = - log_ok(gen_sctp:recv(Sa, infinity)) - end, - ?line ok = gen_sctp:close(Sa), - ?line {Loopback,Pa, - #sctp_shutdown_event{assoc_id=SbAssocId}} = + case log_ok(gen_sctp:recv(Sa, infinity)) of + {Loopback,Pb, + [#sctp_sndrcvinfo{stream=Stream, + assoc_id=SaAssocId}], + Data} -> ok; + Event1 -> + {Loopback,Pb, + #sctp_paddr_change{state=addr_confirmed, + addr={_,Pb}, + error=0, + assoc_id=SaAssocId}} = + recv_event(Event1), + {Loopback,Pb, + [#sctp_sndrcvinfo{stream=Stream, + assoc_id=SaAssocId}], + Data} = + log_ok(gen_sctp:recv(Sa, infinity)) + end, + ok = gen_sctp:close(Sa), + {Loopback,Pa, + #sctp_shutdown_event{assoc_id=SbAssocId}} = recv_event(log_ok(gen_sctp:recv(Sb, infinity))), - ?line {Loopback,Pa, - #sctp_assoc_change{state=shutdown_comp, - error=0, - assoc_id=SbAssocId}} = + {Loopback,Pa, + #sctp_assoc_change{state=shutdown_comp, + error=0, + assoc_id=SbAssocId}} = recv_event(log_ok(gen_sctp:recv(Sb, infinity))), - ?line ok = gen_sctp:close(Sb), + ok = gen_sctp:close(Sb), - ?line receive - Msg -> test_server:fail({received,Msg}) - after 17 -> ok - end, + receive + Msg -> ct:fail({received,Msg}) + after 17 -> ok + end, ok. @@ -1039,8 +1038,7 @@ do_from_other_process(Fun) -> Result -> Parent ! {Ref,Result} catch - Class:Reason -> - Stacktrace = erlang:get_stacktrace(), + Class:Reason:Stacktrace -> Parent ! {Ref,Class,Reason,Stacktrace} end end), @@ -1057,205 +1055,186 @@ do_from_other_process(Fun) -> end. -peeloff_active_once(doc) -> - "Peel off an SCTP stream socket ({active,once})"; -peeloff_active_once(suite) -> - []; +%% Peel off an SCTP stream socket ({active,once}). peeloff_active_once(Config) -> peeloff(Config, [{active,once}]). -peeloff_active_true(doc) -> - "Peel off an SCTP stream socket ({active,true})"; -peeloff_active_true(suite) -> - []; +%% Peel off an SCTP stream socket ({active,true}). peeloff_active_true(Config) -> peeloff(Config, [{active,true}]). -peeloff_active_n(doc) -> - "Peel off an SCTP stream socket ({active,N})"; -peeloff_active_n(suite) -> - []; +%% Peel off an SCTP stream socket ({active,N}). peeloff_active_n(Config) -> peeloff(Config, [{active,1}]). peeloff(Config, SockOpts) when is_list(Config) -> - ?line Addr = {127,0,0,1}, - ?line Stream = 0, - ?line Timeout = 333, - ?line S1 = socket_open([{ifaddr,Addr}|SockOpts], Timeout), - ?line ?LOGVAR(S1), - ?line P1 = socket_call(S1, get_port), - ?line ?LOGVAR(P1), - ?line Socket1 = socket_call(S1, get_socket), - ?line ?LOGVAR(Socket1), - ?line socket_call(S1, {listen,true}), - ?line S2 = socket_open([{ifaddr,Addr}|SockOpts], Timeout), - ?line ?LOGVAR(S2), - ?line P2 = socket_call(S2, get_port), - ?line ?LOGVAR(P2), - ?line Socket2 = socket_call(S2, get_socket), - ?line ?LOGVAR(Socket2), + Addr = {127,0,0,1}, + Stream = 0, + Timeout = 333, + StartTime = timestamp(), + S1 = socket_open([{ifaddr,Addr}|SockOpts], Timeout), + ?LOGVAR(S1), + P1 = socket_call(S1, get_port), + ?LOGVAR(P1), + Socket1 = socket_call(S1, get_socket), + ?LOGVAR(Socket1), + socket_call(S1, {listen,true}), + S2 = socket_open([{ifaddr,Addr}|SockOpts], Timeout), + ?LOGVAR(S2), + P2 = socket_call(S2, get_port), + ?LOGVAR(P2), + Socket2 = socket_call(S2, get_socket), + ?LOGVAR(Socket2), %% - ?line socket_call(S2, {connect_init,Addr,P1,[]}), - ?line S2Ai = + socket_call(S2, {connect_init,Addr,P1,[]}), + S2Ai = receive {S2,{Addr,P1, #sctp_assoc_change{ - state=comm_up, - assoc_id=AssocId2}}} -> AssocId2 + state=comm_up, + assoc_id=AssocId2}}} -> AssocId2 after Timeout -> - socket_bailout([S1,S2]) + socket_bailout([S1,S2], StartTime) end, - ?line ?LOGVAR(S2Ai), - ?line S1Ai = + ?LOGVAR(S2Ai), + S1Ai = receive {S1,{Addr,P2, #sctp_assoc_change{ - state=comm_up, - assoc_id=AssocId1}}} -> AssocId1 + state=comm_up, + assoc_id=AssocId1}}} -> AssocId1 after Timeout -> - socket_bailout([S1,S2]) + socket_bailout([S1,S2], StartTime) end, - ?line ?LOGVAR(S1Ai), + ?LOGVAR(S1Ai), %% - ?line socket_call(S2, {send,S2Ai,Stream,<<"Number one">>}), - ?line - receive - {S1,{Addr,P2,S1Ai,Stream,<<"Number one">>}} -> ok - after Timeout -> - socket_bailout([S1,S2]) - end, - ?line socket_call(S2, {send,Socket1,S1Ai,Stream,<<"Number two">>}), - ?line - receive - {S2,{Addr,P1,S2Ai,Stream,<<"Number two">>}} -> ok - after Timeout -> - socket_bailout([S1,S2]) - end, + socket_call(S2, {send,S2Ai,Stream,<<"Number one">>}), + receive + {S1,{Addr,P2,S1Ai,Stream,<<"Number one">>}} -> ok + after Timeout -> + socket_bailout([S1,S2], StartTime) + end, + socket_call(S2, {send,Socket1,S1Ai,Stream,<<"Number two">>}), + receive + {S2,{Addr,P1,S2Ai,Stream,<<"Number two">>}} -> ok + after Timeout -> + socket_bailout([S1,S2], StartTime) + end, %% - ?line S3 = socket_peeloff(Socket1, S1Ai, SockOpts, Timeout), - ?line ?LOGVAR(S3), - ?line P3_X = socket_call(S3, get_port), - ?line ?LOGVAR(P3_X), - ?line P3 = case P3_X of 0 -> P1; _ -> P3_X end, - ?line [{_,#sctp_paddrinfo{assoc_id=S3Ai,state=active}}] = + S3 = socket_peeloff(Socket1, S1Ai, SockOpts, Timeout), + ?LOGVAR(S3), + P3_X = socket_call(S3, get_port), + ?LOGVAR(P3_X), + P3 = case P3_X of 0 -> P1; _ -> P3_X end, + [{_,#sctp_paddrinfo{assoc_id=S3Ai,state=active}}] = socket_call(S3, {getopts,[{sctp_get_peer_addr_info, #sctp_paddrinfo{address={Addr,P2}}}]}), - %%?line S3Ai = S1Ai, - ?line ?LOGVAR(S3Ai), + %%S3Ai = S1Ai, + ?LOGVAR(S3Ai), %% - ?line socket_call(S3, {send,S3Ai,Stream,<<"Number three">>}), - ?line - receive - {S2,{Addr,P3,S2Ai,Stream,<<"Number three">>}} -> ok - after Timeout -> - socket_bailout([S1,S2,S3]) - end, - ?line socket_call(S3, {send,Socket2,S2Ai,Stream,<<"Number four">>}), - ?line - receive - {S3,{Addr,P2,S3Ai,Stream,<<"Number four">>}} -> ok - after Timeout -> - socket_bailout([S1,S2,S3]) - end, + socket_call(S3, {send,S3Ai,Stream,<<"Number three">>}), + receive + {S2,{Addr,P3,S2Ai,Stream,<<"Number three">>}} -> ok + after Timeout -> + socket_bailout([S1,S2,S3], StartTime) + end, + socket_call(S3, {send,Socket2,S2Ai,Stream,<<"Number four">>}), + receive + {S3,{Addr,P2,S3Ai,Stream,<<"Number four">>}} -> ok + after Timeout -> + socket_bailout([S1,S2,S3], StartTime) + end, %% - ?line inet:i(sctp), - ?line socket_close_verbose(S1), - ?line socket_close_verbose(S2), - ?line - receive - {S3,{Addr,P2,#sctp_shutdown_event{assoc_id=S3Ai_X}}} -> - ?line match_unless_solaris(S3Ai, S3Ai_X) - after Timeout -> - socket_bailout([S3]) - end, - ?line - receive - {S3,{Addr,P2,#sctp_assoc_change{state=shutdown_comp, - assoc_id=S3Ai}}} -> ok - after Timeout -> - socket_bailout([S3]) - end, - ?line socket_close_verbose(S3), - ?line [] = flush(), + inet:i(sctp), + socket_close_verbose(S1, StartTime), + socket_close_verbose(S2, StartTime), + receive + {S3,{Addr,P2,#sctp_shutdown_event{assoc_id=S3Ai_X}}} -> + match_unless_solaris(S3Ai, S3Ai_X) + after Timeout -> + socket_bailout([S3], StartTime) + end, + receive + {S3,{Addr,P2,#sctp_assoc_change{state=shutdown_comp, + assoc_id=S3Ai}}} -> ok + after Timeout -> + socket_bailout([S3], StartTime) + end, + socket_close_verbose(S3, StartTime), + [] = flush(), ok. -buffers(doc) -> - ["Check sndbuf and recbuf behaviour"]; -buffers(suite) -> - []; +%% Check sndbuf and recbuf behaviour. buffers(Config) when is_list(Config) -> - ?line Limit = 4096, - ?line Addr = {127,0,0,1}, - ?line Stream = 1, - ?line Timeout = 3333, - ?line S1 = socket_open([{ip,Addr}], Timeout), - ?line ?LOGVAR(S1), - ?line P1 = socket_call(S1, get_port), - ?line ?LOGVAR(P1), - ?line ok = socket_call(S1, {listen,true}), - ?line S2 = socket_open([{ip,Addr}], Timeout), - ?line ?LOGVAR(S2), - ?line P2 = socket_call(S2, get_port), - ?line ?LOGVAR(P2), + Limit = 4096, + Addr = {127,0,0,1}, + Stream = 1, + Timeout = 3333, + StartTime = timestamp(), + S1 = socket_open([{ip,Addr}], Timeout), + ?LOGVAR(S1), + P1 = socket_call(S1, get_port), + ?LOGVAR(P1), + ok = socket_call(S1, {listen,true}), + S2 = socket_open([{ip,Addr}], Timeout), + ?LOGVAR(S2), + P2 = socket_call(S2, get_port), + ?LOGVAR(P2), %% - ?line socket_call(S2, {connect_init,Addr,P1,[]}), - ?line S2Ai = + socket_call(S2, {connect_init,Addr,P1,[]}), + S2Ai = receive {S2,{Addr,P1, #sctp_assoc_change{ - state=comm_up, - assoc_id=AssocId2}}} -> AssocId2 + state=comm_up, + assoc_id=AssocId2}}} -> AssocId2 after Timeout -> - socket_bailout([S1,S2]) + socket_bailout([S1,S2], StartTime) end, - ?line S1Ai = + S1Ai = receive {S1,{Addr,P2, #sctp_assoc_change{ - state=comm_up, - assoc_id=AssocId1}}} -> AssocId1 + state=comm_up, + assoc_id=AssocId1}}} -> AssocId1 after Timeout -> - socket_bailout([S1,S2]) + socket_bailout([S1,S2], StartTime) end, %% - ?line socket_call(S1, {setopts,[{recbuf,Limit}]}), - ?line Recbuf = + socket_call(S1, {setopts,[{recbuf,Limit}]}), + Recbuf = case socket_call(S1, {getopts,[recbuf]}) of [{recbuf,RB1}] when RB1 >= Limit -> RB1 end, - ?line Data = mk_data(Recbuf+Limit), - ?line socket_call(S2, {setopts,[{sndbuf,Recbuf+Limit}]}), - ?line socket_call(S2, {send,S2Ai,Stream,Data}), - ?line - receive - {S1,{Addr,P2,S1Ai,Stream,Data}} -> ok - after Timeout -> - socket_bailout([S1,S2]) - end, + Data = mk_data(Recbuf+Limit), + socket_call(S2, {setopts,[{sndbuf,Recbuf+Limit}]}), + socket_call(S2, {send,S2Ai,Stream,Data}), + receive + {S1,{Addr,P2,S1Ai,Stream,Data}} -> ok + after Timeout -> + socket_bailout([S1,S2], StartTime) + end, %% - ?line socket_close_verbose(S1), - ?line - receive - {S2,{Addr,P1,#sctp_shutdown_event{assoc_id=S2Ai}}} -> ok - after Timeout -> - socket_bailout([S2]) - end, - ?line - receive - {S2,{Addr,P1,#sctp_assoc_change{state=shutdown_comp, - assoc_id=S2Ai}}} -> ok - after Timeout -> - socket_bailout([S2]) - end, - ?line socket_close_verbose(S2), - ?line [] = flush(), + socket_close_verbose(S1, StartTime), + receive + {S2,{Addr,P1,#sctp_shutdown_event{assoc_id=S2Ai}}} -> ok + after Timeout -> + socket_bailout([S2], StartTime) + end, + receive + {S2,{Addr,P1,#sctp_assoc_change{state=shutdown_comp, + assoc_id=S2Ai}}} -> ok + after Timeout -> + socket_bailout([S2], StartTime) + end, + socket_close_verbose(S2, StartTime), + [] = flush(), ok. mk_data(Bytes) -> @@ -1268,153 +1247,129 @@ mk_data(_, _, Bin) -> -open_multihoming_ipv4_socket(doc) -> - "Test opening a multihoming ipv4 socket"; -open_multihoming_ipv4_socket(suite) -> - []; +%% Test opening a multihoming ipv4 socket. open_multihoming_ipv4_socket(Config) when is_list(Config) -> - ?line case get_addrs_by_family(inet, 2) of - {ok, [Addr1, Addr2]} -> - ?line do_open_and_connect([Addr1, Addr2], Addr1); - {error, Reason} -> - {skip, Reason} - end. - -open_unihoming_ipv6_socket(doc) -> - %% This test is mostly aimed to indicate - %% whether host has a non-working ipv6 setup - "Test opening a unihoming (non-multihoming) ipv6 socket"; -open_unihoming_ipv6_socket(suite) -> - []; + case get_addrs_by_family(inet, 2) of + {ok, [Addr1, Addr2]} -> + do_open_and_connect([Addr1, Addr2], Addr1); + {error, Reason} -> + {skip, Reason} + end. + +%% This test is mostly aimed to indicate whether host has a +%% non-working ipv6 setup. Test opening a unihoming (non-multihoming) +%% ipv6 socket. open_unihoming_ipv6_socket(Config) when is_list(Config) -> - ?line case get_addrs_by_family(inet6, 1) of - {ok, [Addr]} -> - ?line do_open_and_connect([Addr], Addr); - {error, Reason} -> - {skip, Reason} - end. + case get_addrs_by_family(inet6, 1) of + {ok, [Addr]} -> + do_open_and_connect([Addr], Addr); + {error, Reason} -> + {skip, Reason} + end. -open_multihoming_ipv6_socket(doc) -> - "Test opening a multihoming ipv6 socket"; -open_multihoming_ipv6_socket(suite) -> - []; +%% Test opening a multihoming ipv6 socket. open_multihoming_ipv6_socket(Config) when is_list(Config) -> - ?line case get_addrs_by_family(inet6, 2) of - {ok, [Addr1, Addr2]} -> - ?line do_open_and_connect([Addr1, Addr2], Addr1); - {error, Reason} -> - {skip, Reason} - end. - -open_multihoming_ipv4_and_ipv6_socket(doc) -> - "Test opening a multihoming ipv6 socket with ipv4 and ipv6 addresses"; -open_multihoming_ipv4_and_ipv6_socket(suite) -> - []; + case get_addrs_by_family(inet6, 2) of + {ok, [Addr1, Addr2]} -> + do_open_and_connect([Addr1, Addr2], Addr1); + {error, Reason} -> + {skip, Reason} + end. + +%% Test opening a multihoming ipv6 socket with ipv4 and ipv6 addresses. open_multihoming_ipv4_and_ipv6_socket(Config) when is_list(Config) -> - ?line case get_addrs_by_family(inet_and_inet6, 2) of - {ok, [[InetAddr1, InetAddr2], [Inet6Addr1, Inet6Addr2]]} -> - %% Connect to the first address to test bind - ?line do_open_and_connect([InetAddr1, Inet6Addr1, InetAddr2], - InetAddr1), - ?line do_open_and_connect([Inet6Addr1, InetAddr1], - Inet6Addr1), - - %% Connect an address, not the first, - %% to test sctp_bindx - ?line do_open_and_connect([Inet6Addr1, Inet6Addr2, InetAddr1], - Inet6Addr2), - ?line do_open_and_connect([Inet6Addr1, Inet6Addr2, InetAddr1], - InetAddr1); - {error, Reason} -> - {skip, Reason} - end. - -names_unihoming_ipv4(doc) -> - "Test inet:socknames/peernames on unihoming IPv4 sockets"; -names_unihoming_ipv4(suite) -> - []; + case get_addrs_by_family(inet_and_inet6, 2) of + {ok, [[InetAddr1, InetAddr2], [Inet6Addr1, Inet6Addr2]]} -> + %% Connect to the first address to test bind + do_open_and_connect([InetAddr1, Inet6Addr1, InetAddr2], + InetAddr1), + do_open_and_connect([Inet6Addr1, InetAddr1], + Inet6Addr1), + + %% Connect an address, not the first, + %% to test sctp_bindx + do_open_and_connect([Inet6Addr1, Inet6Addr2, InetAddr1], + Inet6Addr2), + do_open_and_connect([Inet6Addr1, Inet6Addr2, InetAddr1], + InetAddr1); + {error, Reason} -> + {skip, Reason} + end. + +%% Test inet:socknames/peernames on unihoming IPv4 sockets. names_unihoming_ipv4(Config) when is_list(Config) -> - ?line do_names(Config, inet, 1). + do_names(Config, inet, 1). -names_unihoming_ipv6(doc) -> - "Test inet:socknames/peernames on unihoming IPv6 sockets"; -names_unihoming_ipv6(suite) -> - []; +%% Test inet:socknames/peernames on unihoming IPv6 sockets. names_unihoming_ipv6(Config) when is_list(Config) -> - ?line do_names(Config, inet6, 1). + do_names(Config, inet6, 1). -names_multihoming_ipv4(doc) -> - "Test inet:socknames/peernames on multihoming IPv4 sockets"; -names_multihoming_ipv4(suite) -> - []; +%% Test inet:socknames/peernames on multihoming IPv4 sockets. names_multihoming_ipv4(Config) when is_list(Config) -> - ?line do_names(Config, inet, 2). + do_names(Config, inet, 2). -names_multihoming_ipv6(doc) -> - "Test inet:socknames/peernames on multihoming IPv6 sockets"; -names_multihoming_ipv6(suite) -> - []; +%% Test inet:socknames/peernames on multihoming IPv6 sockets. names_multihoming_ipv6(Config) when is_list(Config) -> - ?line do_names(Config, inet6, 2). + do_names(Config, inet6, 2). do_names(_, FamilySpec, AddressCount) -> Fun = fun (ServerSocket, _, ServerAssoc, ClientSocket, _, ClientAssoc) -> - ?line ServerSocknamesNoassoc = + ServerSocknamesNoassoc = lists:sort(ok(inet:socknames(ServerSocket))), - ?line ?LOGVAR(ServerSocknamesNoassoc), - ?line ServerSocknames = + ?LOGVAR(ServerSocknamesNoassoc), + ServerSocknames = lists:sort(ok(inet:socknames(ServerSocket, ServerAssoc))), - ?line ?LOGVAR(ServerSocknames), - ?line [_|_] = + ?LOGVAR(ServerSocknames), + [_|_] = ordsets:intersection (ServerSocknamesNoassoc, ServerSocknames), - ?line ClientSocknamesNoassoc = + ClientSocknamesNoassoc = lists:sort(ok(inet:socknames(ClientSocket))), - ?line ?LOGVAR(ClientSocknamesNoassoc), - ?line ClientSocknames = + ?LOGVAR(ClientSocknamesNoassoc), + ClientSocknames = lists:sort(ok(inet:socknames(ClientSocket, ClientAssoc))), - ?line ?LOGVAR(ClientSocknames), - ?line [_|_] = + ?LOGVAR(ClientSocknames), + [_|_] = ordsets:intersection (ClientSocknamesNoassoc, ClientSocknames), - ?line err([einval,enotconn], inet:peernames(ServerSocket)), - ?line ServerPeernames = + err([einval,enotconn], inet:peernames(ServerSocket)), + ServerPeernames = lists:sort(ok(inet:peernames(ServerSocket, ServerAssoc))), - ?line ?LOGVAR(ServerPeernames), - ?line err([einval,enotconn], inet:peernames(ClientSocket)), - ?line ClientPeernames = + ?LOGVAR(ServerPeernames), + err([einval,enotconn], inet:peernames(ClientSocket)), + ClientPeernames = lists:sort(ok(inet:peernames(ClientSocket, ClientAssoc))), - ?line ?LOGVAR(ClientPeernames), - ?line ServerSocknames = ClientPeernames, - ?line ClientSocknames = ServerPeernames, - ?line {ok,Socket} = + ?LOGVAR(ClientPeernames), + ServerSocknames = ClientPeernames, + ClientSocknames = ServerPeernames, + {ok,Socket} = gen_sctp:peeloff(ServerSocket, ServerAssoc), - ?line SocknamesNoassoc = + SocknamesNoassoc = lists:sort(ok(inet:socknames(Socket))), - ?line ?LOGVAR(SocknamesNoassoc), - ?line Socknames = + ?LOGVAR(SocknamesNoassoc), + Socknames = lists:sort(ok(inet:socknames(Socket, ServerAssoc))), - ?line ?LOGVAR(Socknames), - ?line true = + ?LOGVAR(Socknames), + true = ordsets:is_subset(SocknamesNoassoc, Socknames), - ?line Peernames = + Peernames = lists:sort(ok(inet:peernames(Socket, ServerAssoc))), - ?line ?LOGVAR(Peernames), - ?line ok = gen_sctp:close(Socket), - ?line Socknames = ClientPeernames, - ?line ClientSocknames = Peernames, + ?LOGVAR(Peernames), + ok = gen_sctp:close(Socket), + Socknames = ClientPeernames, + ClientSocknames = Peernames, ok end, - ?line case get_addrs_by_family(FamilySpec, AddressCount) of - {ok, Addresses} when length(Addresses) =:= AddressCount -> - ?line do_open_and_connect(Addresses, hd(Addresses), Fun); - {error, Reason} -> - {skip, Reason} - end. + case get_addrs_by_family(FamilySpec, AddressCount) of + {ok, Addresses} when length(Addresses) =:= AddressCount -> + do_open_and_connect(Addresses, hd(Addresses), Fun); + {error, Reason} -> + {skip, Reason} + end. @@ -1450,29 +1405,28 @@ get_addrs_by_family(Family, NumAddrs) -> get_addrs_by_family_aux(Family, NumAddrs) when Family =:= inet; Family =:= inet6 -> - ?line - case inet:getaddr(localhost, Family) of - {error,eafnosupport} -> - {skip, f("No support for ~p", Family)}; - {ok, _} -> - ?line IfAddrs = ok(inet:getifaddrs()), - ?line case filter_addrs_by_family(IfAddrs, Family) of - Addrs when length(Addrs) >= NumAddrs -> - {ok, lists:sublist(Addrs, NumAddrs)}; - [] -> - {error, f("Need ~p ~p address(es) found none~n", - [NumAddrs, Family])}; - Addrs -> - {error, - f("Need ~p ~p address(es) found only ~p: ~p~n", - [NumAddrs, Family, length(Addrs), Addrs])} - end - end; + case inet:getaddr(localhost, Family) of + {error,eafnosupport} -> + {skip, f("No support for ~p", Family)}; + {ok, _} -> + IfAddrs = ok(inet:getifaddrs()), + case filter_addrs_by_family(IfAddrs, Family) of + Addrs when length(Addrs) >= NumAddrs -> + {ok, lists:sublist(Addrs, NumAddrs)}; + [] -> + {error, f("Need ~p ~p address(es) found none~n", + [NumAddrs, Family])}; + Addrs -> + {error, + f("Need ~p ~p address(es) found only ~p: ~p~n", + [NumAddrs, Family, length(Addrs), Addrs])} + end + end; get_addrs_by_family_aux(inet_and_inet6, NumAddrs) -> - ?line catch {ok, [case get_addrs_by_family_aux(Family, NumAddrs) of - {ok, Addrs} -> Addrs; - {error, Reason} -> throw({error, Reason}) - end || Family <- [inet, inet6]]}. + catch {ok, [case get_addrs_by_family_aux(Family, NumAddrs) of + {ok, Addrs} -> Addrs; + {error, Reason} -> throw({error, Reason}) + end || Family <- [inet, inet6]]}. filter_addrs_by_family(IfAddrs, Family) -> lists:flatten([[Addr || {addr, Addr} <- Info, @@ -1501,21 +1455,21 @@ f(F, A) -> lists:flatten(io_lib:format(F, A)). do_open_and_connect(ServerAddresses, AddressToConnectTo) -> - ?line Fun = fun (_, _, _, _, _, _) -> ok end, - ?line do_open_and_connect(ServerAddresses, AddressToConnectTo, Fun). + Fun = fun (_, _, _, _, _, _) -> ok end, + do_open_and_connect(ServerAddresses, AddressToConnectTo, Fun). %% do_open_and_connect(ServerAddresses, AddressToConnectTo, Fun) -> - ?line ServerFamily = get_family_by_addrs(ServerAddresses), - ?line io:format("Serving ~p addresses: ~p~n", - [ServerFamily, ServerAddresses]), - ?line S1 = ok(gen_sctp:open(0, [{ip,Addr} || Addr <- ServerAddresses] ++ - [ServerFamily])), - ?line ok = gen_sctp:listen(S1, true), - ?line P1 = ok(inet:port(S1)), - ?line ClientFamily = get_family_by_addr(AddressToConnectTo), - ?line io:format("Connecting to ~p ~p~n", - [ClientFamily, AddressToConnectTo]), - ?line ClientOpts = + ServerFamily = get_family_by_addrs(ServerAddresses), + io:format("Serving ~p addresses: ~p~n", + [ServerFamily, ServerAddresses]), + S1 = ok(gen_sctp:open(0, [{ip,Addr} || Addr <- ServerAddresses] ++ + [ServerFamily])), + ok = gen_sctp:listen(S1, true), + P1 = ok(inet:port(S1)), + ClientFamily = get_family_by_addr(AddressToConnectTo), + io:format("Connecting to ~p ~p~n", + [ClientFamily, AddressToConnectTo]), + ClientOpts = [ClientFamily | case ClientFamily of inet6 -> @@ -1523,39 +1477,39 @@ do_open_and_connect(ServerAddresses, AddressToConnectTo, Fun) -> _ -> [] end], - ?line S2 = ok(gen_sctp:open(0, ClientOpts)), + S2 = ok(gen_sctp:open(0, ClientOpts)), log(open), %% Verify client can connect - ?line #sctp_assoc_change{state=comm_up} = S2Assoc = + #sctp_assoc_change{state=comm_up} = S2Assoc = ok(gen_sctp:connect(S2, AddressToConnectTo, P1, [])), log(comm_up), %% verify server side also receives comm_up from client - ?line S1Assoc = recv_comm_up_eventually(S1), - ?line Result = Fun(S1, ServerFamily, S1Assoc, S2, ClientFamily, S2Assoc), - ?line ok = gen_sctp:close(S2), - ?line ok = gen_sctp:close(S1), + S1Assoc = recv_comm_up_eventually(S1), + Result = Fun(S1, ServerFamily, S1Assoc, S2, ClientFamily, S2Assoc), + ok = gen_sctp:close(S2), + ok = gen_sctp:close(S1), Result. %% If at least one of the addresses is an ipv6 address, return inet6, else inet. get_family_by_addrs(Addresses) -> - ?line case lists:usort([get_family_by_addr(Addr) || Addr <- Addresses]) of - [inet, inet6] -> inet6; - [inet] -> inet; - [inet6] -> inet6 - end. + case lists:usort([get_family_by_addr(Addr) || Addr <- Addresses]) of + [inet, inet6] -> inet6; + [inet] -> inet; + [inet6] -> inet6 + end. get_family_by_addr(Addr) when tuple_size(Addr) =:= 4 -> inet; get_family_by_addr(Addr) when tuple_size(Addr) =:= 8 -> inet6. recv_comm_up_eventually(S) -> - ?line case ok(gen_sctp:recv(S)) of - {_Addr, _Port, _Info, - #sctp_assoc_change{state=comm_up} = Assoc} -> - Assoc; - {_Addr, _Port, _Info, _OtherSctpMsg} = Msg -> - ?line log({unexpected,Msg}), - ?line recv_comm_up_eventually(S) - end. + case ok(gen_sctp:recv(S)) of + {_Addr, _Port, _Info, + #sctp_assoc_change{state=comm_up} = Assoc} -> + Assoc; + {_Addr, _Port, _Info, _OtherSctpMsg} = Msg -> + log({unexpected,Msg}), + recv_comm_up_eventually(S) + end. %%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% socket gen_server ultra light @@ -1588,8 +1542,8 @@ socket_peeloff(Socket, AssocId, SocketOpts, Timeout) -> end, s_start(Starter, Timeout). -socket_close_verbose(S) -> - History = socket_history(socket_close(S)), +socket_close_verbose(S, StartTime) -> + History = socket_history(socket_close(S), StartTime), io:format("socket_close ~p:~n ~p.~n", [S,History]), History. @@ -1602,19 +1556,19 @@ socket_call(S, Request) -> %% socket_get(S, Key) -> %% s_req(S, {get,Key}). -socket_bailout([S|Ss]) -> - History = socket_history(socket_close(S)), +socket_bailout([S|Ss], StartTime) -> + History = socket_history(socket_close(S), StartTime), io:format("bailout ~p:~n ~p.~n", [S,History]), - socket_bailout(Ss); -socket_bailout([]) -> + socket_bailout(Ss, StartTime); +socket_bailout([], _) -> io:format("flush: ~p.~n", [flush()]), - test_server:fail(socket_bailout). + ct:fail(socket_bailout). -socket_history({State,Flush}) -> +socket_history({State,Flush}, StartTime) -> {lists:keysort( 2, lists:flatten( - [[{Key,Val} || Val <- Vals] + [[{Key,{TS-StartTime,Val}} || {TS,Val} <- Vals] || {Key,Vals} <- gb_trees:to_list(State)])), Flush}. @@ -1662,8 +1616,7 @@ s_start(Socket, Timeout, Parent) -> try s_loop(Socket, Timeout, Parent, Handler, gb_trees:empty()) catch - Class:Reason -> - Stacktrace = erlang:get_stacktrace(), + Class:Reason:Stacktrace -> io:format(?MODULE_STRING":socket exception ~w:~w at~n" "~p.~n", [Class,Reason,Stacktrace]), erlang:raise(Class, Reason, Stacktrace) @@ -1677,14 +1630,12 @@ s_loop(Socket, Timeout, Parent, Handler, State) -> {Parent,Ref,exit} -> ok = gen_sctp:close(Socket), Key = exit, - Val = {now(),Socket}, - NewState = gb_push(Key, Val, State), + NewState = gb_push(Key, Socket, State), Parent ! {self(),Ref,{NewState,flush()}}; {Parent,Ref,{Msg}} -> Result = Handler(Msg), Key = req, - Val = {now(),{Msg,Result}}, - NewState = gb_push(Key, Val, State), + NewState = gb_push(Key, {Msg,Result}, State), Parent ! {self(),Ref,Result}, s_loop(Socket, Timeout, Parent, Handler, NewState); %% {Parent,Ref,{get,Key}} -> @@ -1694,16 +1645,15 @@ s_loop(Socket, Timeout, Parent, Handler, State) -> {[#sctp_sndrcvinfo{stream=Stream,assoc_id=AssocId}=SRI],Data}} when not is_tuple(Data) -> case gb_get({assoc_change,AssocId}, State) of - [{_,{Addr,Port, - #sctp_assoc_change{ - state=comm_up, - inbound_streams=Is}}}|_] + [{Addr,Port, + #sctp_assoc_change{ + state=comm_up, + inbound_streams=Is}}|_] when 0 =< Stream, Stream < Is-> ok; [] -> ok end, Key = {msg,AssocId,Stream}, - Val = {now(),{Addr,Port,SRI,Data}}, - NewState = gb_push(Key, Val, State), + NewState = gb_push(Key, {Addr,Port,SRI,Data}, State), Parent ! {self(),{Addr,Port,AssocId,Stream,Data}}, again(Socket), s_loop(Socket, Timeout, Parent, Handler, NewState); @@ -1714,13 +1664,12 @@ s_loop(Socket, Timeout, Parent, Handler, State) -> [] -> ok end, Key = {assoc_change,AssocId}, - Val = {now(),{Addr,Port,SAC}}, case {gb_get(Key, State),St} of {[],_} -> ok; - {[{_,{Addr,Port,#sctp_assoc_change{state=comm_up}}}|_],_} + {[{Addr,Port,#sctp_assoc_change{state=comm_up}}|_],_} when St =:= comm_lost; St =:= shutdown_comp -> ok end, - NewState = gb_push(Key, Val, State), + NewState = gb_push(Key, {Addr,Port,SAC}, State), Parent ! {self(),{Addr,Port,SAC}}, again(Socket), s_loop(Socket, Timeout, Parent, Handler, NewState); @@ -1734,14 +1683,13 @@ s_loop(Socket, Timeout, Parent, Handler, State) -> [] -> ok end, case {gb_get({assoc_change,AssocId}, State),St} of - {[{_,{Addr,Port,#sctp_assoc_change{state=comm_up}}}|_],_} + {[{Addr,Port,#sctp_assoc_change{state=comm_up}}|_],_} when St =:= addr_available; St =:= addr_confirmed -> ok; {[],addr_confirmed} -> ok end, Key = {paddr_change,AssocId}, - Val = {now(),{Addr,Port,SPC}}, - NewState = gb_push(Key, Val, State), + NewState = gb_push(Key, {Addr,Port,SPC}, State), again(Socket), s_loop(Socket, Timeout, Parent, Handler, NewState); {sctp,Socket,Addr,Port, @@ -1751,12 +1699,11 @@ s_loop(Socket, Timeout, Parent, Handler, State) -> [] -> ok end, case gb_get({assoc_change,AssocId}, State) of - [{_,{Addr,Port,#sctp_assoc_change{state=comm_up}}}|_] -> ok; + [{Addr,Port,#sctp_assoc_change{state=comm_up}}|_] -> ok; [] -> ok end, Key = {shutdown_event,AssocId}, - Val = {now(),{Addr,Port}}, - NewState = gb_push(Key, Val, State), + NewState = gb_push(Key, {Addr,Port}, State), Parent ! {self(), {Addr,Port,SSE}}, again(Socket), s_loop(Socket, Timeout, Parent, Handler, NewState); @@ -1774,11 +1721,12 @@ again(Socket) -> end. gb_push(Key, Val, GBT) -> + TS = timestamp(), case gb_trees:lookup(Key, GBT) of none -> - gb_trees:insert(Key, [Val], GBT); + gb_trees:insert(Key, [{TS,Val}], GBT); {value,V} -> - gb_trees:update(Key, [Val|V], GBT) + gb_trees:update(Key, [{TS,Val}|V], GBT) end. gb_get(Key, GBT) -> @@ -1786,7 +1734,7 @@ gb_get(Key, GBT) -> none -> []; {value,V} -> - V + [Val || {_TS,Val} <- V] end. match_unless_solaris(A, B) -> @@ -1794,3 +1742,6 @@ match_unless_solaris(A, B) -> {unix,sunos} -> B; _ -> A = B end. + +timestamp() -> + erlang:monotonic_time(). diff --git a/lib/kernel/test/gen_tcp_api_SUITE.erl b/lib/kernel/test/gen_tcp_api_SUITE.erl index c27d265550..1be016444f 100644 --- a/lib/kernel/test/gen_tcp_api_SUITE.erl +++ b/lib/kernel/test/gen_tcp_api_SUITE.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2011. All Rights Reserved. +%% Copyright Ericsson AB 1998-2018. 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% @@ -30,23 +31,37 @@ init_per_testcase/2, end_per_testcase/2, t_connect_timeout/1, t_accept_timeout/1, t_connect_bad/1, - t_recv_timeout/1, t_recv_eof/1, + t_recv_timeout/1, t_recv_eof/1, t_recv_delim/1, t_shutdown_write/1, t_shutdown_both/1, t_shutdown_error/1, - t_fdopen/1, t_fdconnect/1, t_implicit_inet6/1]). + t_shutdown_async/1, + t_fdopen/1, t_fdconnect/1, t_implicit_inet6/1, + t_local_basic/1, t_local_unbound/1, t_local_fdopen/1, + t_local_fdopen_listen/1, t_local_fdopen_listen_unbound/1, + t_local_fdopen_connect/1, t_local_fdopen_connect_unbound/1, + t_local_abstract/1, t_accept_inet6_tclass/1]). -export([getsockfd/0,closesockfd/1]). -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap,{minutes,1}}]. all() -> [{group, t_accept}, {group, t_connect}, {group, t_recv}, t_shutdown_write, t_shutdown_both, t_shutdown_error, - t_fdopen, t_fdconnect, t_implicit_inet6]. + t_shutdown_async, t_fdopen, t_fdconnect, t_implicit_inet6, + t_accept_inet6_tclass, + {group, t_local}]. groups() -> [{t_accept, [], [t_accept_timeout]}, {t_connect, [], [t_connect_timeout, t_connect_bad]}, - {t_recv, [], [t_recv_timeout, t_recv_eof]}]. + {t_recv, [], [t_recv_timeout, t_recv_eof, t_recv_delim]}, + {t_local, [], + [t_local_basic, t_local_unbound, t_local_fdopen, + t_local_fdopen_listen, t_local_fdopen_listen_unbound, + t_local_fdopen_connect, t_local_fdopen_connect_unbound, + t_local_abstract]}]. @@ -56,145 +71,199 @@ init_per_suite(Config) -> end_per_suite(_Config) -> ok. +init_per_group(t_local, Config) -> + case gen_tcp:connect({local,<<"/">>}, 0, []) of + {error,eafnosupport} -> + {skip, "AF_LOCAL not supported"}; + {error,_} -> + Config + end; init_per_group(_GroupName, Config) -> Config. -end_per_group(_,_Config) -> +end_per_group(t_local, _Config) -> + delete_local_filenames(); +end_per_group(_, _Config) -> ok. + +init_per_testcase(Func, Config) + when Func =:= undefined -> % Insert your testcase name here + dbg:tracer(), + dbg:p(self(), c), + dbg:tpl(prim_inet, cx), + dbg:tpl(local_tcp, cx), + dbg:tpl(inet, cx), + dbg:tpl(gen_tcp, cx), + Config; init_per_testcase(_Func, Config) -> - Dog = test_server:timetrap(test_server:seconds(60)), - [{watchdog, Dog}|Config]. -end_per_testcase(_Func, Config) -> - Dog = ?config(watchdog, Config), - test_server:timetrap_cancel(Dog). + Config. + +end_per_testcase(_Func, _Config) -> + dbg:stop(). %%% gen_tcp:accept/1,2 -t_accept_timeout(doc) -> "Test that gen_tcp:accept/2 (with timeout) works."; -t_accept_timeout(suite) -> []; +%% Test that gen_tcp:accept/2 (with timeout) works. t_accept_timeout(Config) when is_list(Config) -> - ?line {ok, L} = gen_tcp:listen(0, []), - ?line timeout({gen_tcp, accept, [L, 200]}, 0.2, 1.0). + {ok, L} = gen_tcp:listen(0, []), + timeout({gen_tcp, accept, [L, 200]}, 0.2, 1.0). %%% gen_tcp:connect/X -t_connect_timeout(doc) -> "Test that gen_tcp:connect/4 (with timeout) works."; +%% Test that gen_tcp:connect/4 (with timeout) works. t_connect_timeout(Config) when is_list(Config) -> - %%?line BadAddr = {134,138,177,16}, - %%?line TcpPort = 80, - ?line {ok, BadAddr} = unused_ip(), - ?line TcpPort = 45638, - ?line ok = io:format("Connecting to ~p, port ~p", [BadAddr, TcpPort]), - ?line connect_timeout({gen_tcp,connect,[BadAddr,TcpPort,[],200]}, 0.2, 5.0). - -t_connect_bad(doc) -> - ["Test that gen_tcp:connect/3 handles non-existings hosts, and other ", - "invalid things."]; -t_connect_bad(suite) -> []; + %%BadAddr = {134,138,177,16}, + %%TcpPort = 80, + {ok, BadAddr} = unused_ip(), + TcpPort = 45638, + ok = io:format("Connecting to ~p, port ~p", [BadAddr, TcpPort]), + connect_timeout({gen_tcp,connect,[BadAddr,TcpPort,[],200]}, 0.2, 5.0). + +%% Test that gen_tcp:connect/3 handles non-existings hosts, and other +%% invalid things. t_connect_bad(Config) when is_list(Config) -> - ?line NonExistingPort = 45638, % Not in use, I hope. - ?line {error, Reason1} = gen_tcp:connect(localhost, NonExistingPort, []), - ?line io:format("Error for connection attempt to port not in use: ~p", - [Reason1]), - - ?line {error, Reason2} = gen_tcp:connect("non-existing-host-xxx", 7, []), - ?line io:format("Error for connection attempt to non-existing host: ~p", - [Reason2]), + NonExistingPort = 45638, % Not in use, I hope. + {error, Reason1} = gen_tcp:connect(localhost, NonExistingPort, []), + io:format("Error for connection attempt to port not in use: ~p", + [Reason1]), + + {error, Reason2} = gen_tcp:connect("non-existing-host-xxx", 7, []), + io:format("Error for connection attempt to non-existing host: ~p", + [Reason2]), ok. %%% gen_tcp:recv/X -t_recv_timeout(doc) -> "Test that gen_tcp:recv/3 (with timeout works)."; -t_recv_timeout(suite) -> []; +%% Test that gen_tcp:recv/3 (with timeout works). t_recv_timeout(Config) when is_list(Config) -> - ?line {ok, L} = gen_tcp:listen(0, []), - ?line {ok, Port} = inet:port(L), - ?line {ok, Client} = gen_tcp:connect(localhost, Port, [{active, false}]), - ?line {ok, _A} = gen_tcp:accept(L), - ?line timeout({gen_tcp, recv, [Client, 0, 200]}, 0.2, 5.0). - -t_recv_eof(doc) -> "Test that end of file on a socket is reported correctly."; -t_recv_eof(suite) -> []; + {ok, L} = gen_tcp:listen(0, []), + {ok, Port} = inet:port(L), + {ok, Client} = gen_tcp:connect(localhost, Port, [{active, false}]), + {ok, _A} = gen_tcp:accept(L), + timeout({gen_tcp, recv, [Client, 0, 200]}, 0.2, 5.0). + +%% Test that end of file on a socket is reported correctly. t_recv_eof(Config) when is_list(Config) -> - ?line {ok, L} = gen_tcp:listen(0, []), - ?line {ok, Port} = inet:port(L), - ?line {ok, Client} = gen_tcp:connect(localhost, Port, [{active, false}]), - ?line {ok, A} = gen_tcp:accept(L), - ?line ok = gen_tcp:close(A), - ?line {error, closed} = gen_tcp:recv(Client, 0), + {ok, L} = gen_tcp:listen(0, []), + {ok, Port} = inet:port(L), + {ok, Client} = gen_tcp:connect(localhost, Port, [{active, false}]), + {ok, A} = gen_tcp:accept(L), + ok = gen_tcp:close(A), + {error, closed} = gen_tcp:recv(Client, 0), + ok. + +%% Test using message delimiter $X. +t_recv_delim(Config) when is_list(Config) -> + {ok, L} = gen_tcp:listen(0, []), + {ok, Port} = inet:port(L), + Opts = [{active,false},{packet,line},{line_delimiter,$X}], + {ok, Client} = gen_tcp:connect(localhost, Port, Opts), + {ok, A} = gen_tcp:accept(L), + ok = gen_tcp:send(A, "abcXefgX"), + {ok, "abcX"} = gen_tcp:recv(Client, 0, 200), + {ok, "efgX"} = gen_tcp:recv(Client, 0, 200), + ok = gen_tcp:close(Client), + ok = gen_tcp:close(A), ok. %%% gen_tcp:shutdown/2 t_shutdown_write(Config) when is_list(Config) -> - ?line {ok, L} = gen_tcp:listen(0, []), - ?line {ok, Port} = inet:port(L), - ?line {ok, Client} = gen_tcp:connect(localhost, Port, [{active, false}]), - ?line {ok, A} = gen_tcp:accept(L), - ?line ok = gen_tcp:shutdown(A, write), - ?line {error, closed} = gen_tcp:recv(Client, 0), + {ok, L} = gen_tcp:listen(0, []), + {ok, Port} = inet:port(L), + {ok, Client} = gen_tcp:connect(localhost, Port, [{active, false}]), + {ok, A} = gen_tcp:accept(L), + ok = gen_tcp:shutdown(A, write), + {error, closed} = gen_tcp:recv(Client, 0), ok. t_shutdown_both(Config) when is_list(Config) -> - ?line {ok, L} = gen_tcp:listen(0, []), - ?line {ok, Port} = inet:port(L), - ?line {ok, Client} = gen_tcp:connect(localhost, Port, [{active, false}]), - ?line {ok, A} = gen_tcp:accept(L), - ?line ok = gen_tcp:shutdown(A, read_write), - ?line {error, closed} = gen_tcp:recv(Client, 0), + {ok, L} = gen_tcp:listen(0, []), + {ok, Port} = inet:port(L), + {ok, Client} = gen_tcp:connect(localhost, Port, [{active, false}]), + {ok, A} = gen_tcp:accept(L), + ok = gen_tcp:shutdown(A, read_write), + {error, closed} = gen_tcp:recv(Client, 0), ok. t_shutdown_error(Config) when is_list(Config) -> - ?line {ok, L} = gen_tcp:listen(0, []), - ?line {error, enotconn} = gen_tcp:shutdown(L, read_write), - ?line ok = gen_tcp:close(L), - ?line {error, closed} = gen_tcp:shutdown(L, read_write), + {ok, L} = gen_tcp:listen(0, []), + {error, enotconn} = gen_tcp:shutdown(L, read_write), + ok = gen_tcp:close(L), + {error, closed} = gen_tcp:shutdown(L, read_write), ok. - + +t_shutdown_async(Config) when is_list(Config) -> + {OS, _} = os:type(), + {ok, L} = gen_tcp:listen(0, [{sndbuf, 4096}]), + {ok, Port} = inet:port(L), + {ok, Client} = gen_tcp:connect(localhost, Port, + [{recbuf, 4096}, + {active, false}]), + {ok, S} = gen_tcp:accept(L), + PayloadSize = 1024 * 1024, + Payload = lists:duplicate(PayloadSize, $.), + ok = gen_tcp:send(S, Payload), + case erlang:port_info(S, queue_size) of + {queue_size, N} when N > 0 -> ok; + {queue_size, 0} when OS =:= win32 -> ok; + {queue_size, 0} = T -> ct:fail({unexpected, T}) + end, + + ok = gen_tcp:shutdown(S, write), + {ok, Buf} = gen_tcp:recv(Client, PayloadSize), + {error, closed} = gen_tcp:recv(Client, 0), + case length(Buf) of + PayloadSize -> ok; + Sz -> ct:fail({payload_size, + {expected, PayloadSize}, + {received, Sz}}) + end. + %%% gen_tcp:fdopen/2 t_fdopen(Config) when is_list(Config) -> - ?line Question = "Aaaa... Long time ago in a small town in Germany,", - ?line Question1 = list_to_binary(Question), - ?line Question2 = [<<"Aaaa">>, "... ", $L, <<>>, $o, "ng time ago ", - ["in ", [], <<"a small town">>, [" in Germany,", <<>>]]], - ?line Question1 = iolist_to_binary(Question2), - ?line Answer = "there was a shoemaker, Schumacher was his name.", - ?line {ok, L} = gen_tcp:listen(0, [{active, false}]), - ?line {ok, Port} = inet:port(L), - ?line {ok, Client} = gen_tcp:connect(localhost, Port, [{active, false}]), - ?line {ok, A} = gen_tcp:accept(L), - ?line {ok, FD} = prim_inet:getfd(A), - ?line {ok, Server} = gen_tcp:fdopen(FD, []), - ?line ok = gen_tcp:send(Client, Question), - ?line {ok, Question} = gen_tcp:recv(Server, length(Question), 2000), - ?line ok = gen_tcp:send(Client, Question1), - ?line {ok, Question} = gen_tcp:recv(Server, length(Question), 2000), - ?line ok = gen_tcp:send(Client, Question2), - ?line {ok, Question} = gen_tcp:recv(Server, length(Question), 2000), - ?line ok = gen_tcp:send(Server, Answer), - ?line {ok, Answer} = gen_tcp:recv(Client, length(Answer), 2000), - ?line ok = gen_tcp:close(Client), - ?line {error,closed} = gen_tcp:recv(A, 1, 2000), - ?line ok = gen_tcp:close(Server), - ?line ok = gen_tcp:close(A), - ?line ok = gen_tcp:close(L), + Question = "Aaaa... Long time ago in a small town in Germany,", + Question1 = list_to_binary(Question), + Question2 = [<<"Aaaa">>, "... ", $L, <<>>, $o, "ng time ago ", + ["in ", [], <<"a small town">>, [" in Germany,", <<>>]]], + Question1 = iolist_to_binary(Question2), + Answer = "there was a shoemaker, Schumacher was his name.", + {ok, L} = gen_tcp:listen(0, [{active, false}]), + {ok, Port} = inet:port(L), + {ok, Client} = gen_tcp:connect(localhost, Port, [{active, false}]), + {ok, A} = gen_tcp:accept(L), + {ok, FD} = prim_inet:getfd(A), + {ok, Server} = gen_tcp:fdopen(FD, []), + ok = gen_tcp:send(Client, Question), + {ok, Question} = gen_tcp:recv(Server, length(Question), 2000), + ok = gen_tcp:send(Client, Question1), + {ok, Question} = gen_tcp:recv(Server, length(Question), 2000), + ok = gen_tcp:send(Client, Question2), + {ok, Question} = gen_tcp:recv(Server, length(Question), 2000), + ok = gen_tcp:send(Server, Answer), + {ok, Answer} = gen_tcp:recv(Client, length(Answer), 2000), + ok = gen_tcp:close(Client), + {error,closed} = gen_tcp:recv(A, 1, 2000), + ok = gen_tcp:close(Server), + ok = gen_tcp:close(A), + ok = gen_tcp:close(L), ok. t_fdconnect(Config) when is_list(Config) -> Question = "Aaaa... Long time ago in a small town in Germany,", Question1 = list_to_binary(Question), Question2 = [<<"Aaaa">>, "... ", $L, <<>>, $o, "ng time ago ", - ["in ", [], <<"a small town">>, [" in Germany,", <<>>]]], + ["in ", [], <<"a small town">>, [" in Germany,", <<>>]]], Question1 = iolist_to_binary(Question2), Answer = "there was a shoemaker, Schumacher was his name.", - Path = ?config(data_dir, Config), + Path = proplists:get_value(data_dir, Config), Lib = "gen_tcp_api_SUITE", ok = erlang:load_nif(filename:join(Path,Lib), []), {ok, L} = gen_tcp:listen(0, [{active, false}]), @@ -222,53 +291,253 @@ t_fdconnect(Config) when is_list(Config) -> %%% implicit inet6 option to api functions t_implicit_inet6(Config) when is_list(Config) -> - ?line Host = ok(inet:gethostname()), - ?line - case inet:getaddr(Host, inet6) of - {ok,Addr} -> - ?line t_implicit_inet6(Host, Addr); - {error,Reason} -> - {skip, - "Can not look up IPv6 address: " - ++atom_to_list(Reason)} - end. + Host = ok(inet:gethostname()), + case inet:getaddr(Host, inet6) of + {ok,Addr} -> + t_implicit_inet6(Host, Addr); + {error,Reason} -> + {skip, + "Can not look up IPv6 address: " + ++atom_to_list(Reason)} + end. t_implicit_inet6(Host, Addr) -> - ?line - case gen_tcp:listen(0, [inet6]) of - {ok,S1} -> - ?line Loopback = {0,0,0,0,0,0,0,1}, - ?line io:format("~s ~p~n", ["::1",Loopback]), - ?line implicit_inet6(S1, Loopback), - ?line ok = gen_tcp:close(S1), - %% - ?line Localhost = "localhost", - ?line Localaddr = ok(inet:getaddr(Localhost, inet6)), - ?line io:format("~s ~p~n", [Localhost,Localaddr]), - ?line S2 = ok(gen_tcp:listen(0, [{ip,Localaddr}])), - ?line implicit_inet6(S2, Localaddr), - ?line ok = gen_tcp:close(S2), - %% - ?line io:format("~s ~p~n", [Host,Addr]), - ?line S3 = ok(gen_tcp:listen(0, [{ifaddr,Addr}])), - ?line implicit_inet6(S3, Addr), - ?line ok = gen_tcp:close(S3); - {error,_} -> - {skip,"IPv6 not supported"} - end. + Loopback = {0,0,0,0,0,0,0,1}, + case gen_tcp:listen(0, [inet6, {ip,Loopback}]) of + {ok,S1} -> + io:format("~s ~p~n", ["::1",Loopback]), + implicit_inet6(S1, Loopback), + ok = gen_tcp:close(S1), + %% + Localaddr = ok(get_localaddr()), + S2 = ok(gen_tcp:listen(0, [{ip,Localaddr}])), + implicit_inet6(S2, Localaddr), + ok = gen_tcp:close(S2), + %% + io:format("~s ~p~n", [Host,Addr]), + S3 = ok(gen_tcp:listen(0, [{ifaddr,Addr}])), + implicit_inet6(S3, Addr), + ok = gen_tcp:close(S3); + {error,_} -> + {skip,"IPv6 not supported"} + end. implicit_inet6(S, Addr) -> - ?line P = ok(inet:port(S)), - ?line S2 = ok(gen_tcp:connect(Addr, P, [])), - ?line P2 = ok(inet:port(S2)), - ?line S1 = ok(gen_tcp:accept(S)), - ?line P1 = P = ok(inet:port(S1)), - ?line {Addr,P2} = ok(inet:peername(S1)), - ?line {Addr,P1} = ok(inet:peername(S2)), - ?line {Addr,P1} = ok(inet:sockname(S1)), - ?line {Addr,P2} = ok(inet:sockname(S2)), - ?line ok = gen_tcp:close(S2), - ?line ok = gen_tcp:close(S1). + P = ok(inet:port(S)), + S2 = ok(gen_tcp:connect(Addr, P, [])), + P2 = ok(inet:port(S2)), + S1 = ok(gen_tcp:accept(S)), + P1 = P = ok(inet:port(S1)), + {Addr,P2} = ok(inet:peername(S1)), + {Addr,P1} = ok(inet:peername(S2)), + {Addr,P1} = ok(inet:sockname(S1)), + {Addr,P2} = ok(inet:sockname(S2)), + ok = gen_tcp:close(S2), + ok = gen_tcp:close(S1). + + + +t_local_basic(_Config) -> + SFile = local_filename(server), + SAddr = {local,bin_filename(SFile)}, + CFile = local_filename(client), + CAddr = {local,bin_filename(CFile)}, + _ = file:delete(SFile), + _ = file:delete(CFile), + %% + L = + ok( + gen_tcp:listen(0, [{ifaddr,{local,SFile}},{active,false}])), + C = + ok( + gen_tcp:connect( + {local,SFile}, 0, [{ifaddr,{local,CFile}},{active,false}])), + S = ok(gen_tcp:accept(L)), + SAddr = ok(inet:sockname(L)), + {error,enotconn} = inet:peername(L), + local_handshake(S, SAddr, C, CAddr), + ok = gen_tcp:close(L), + ok = gen_tcp:close(S), + ok = gen_tcp:close(C), + %% + ok = file:delete(SFile), + ok = file:delete(CFile), + ok. + +t_local_unbound(_Config) -> + SFile = local_filename(server), + SAddr = {local,bin_filename(SFile)}, + _ = file:delete(SFile), + %% + L = ok(gen_tcp:listen(0, [{ifaddr,SAddr},{active,false}])), + C = ok(gen_tcp:connect(SAddr, 0, [{active,false}])), + S = ok(gen_tcp:accept(L)), + SAddr = ok(inet:sockname(L)), + {error,enotconn} = inet:peername(L), + local_handshake(S, SAddr, C, {local,<<>>}), + ok = gen_tcp:close(L), + ok = gen_tcp:close(S), + ok = gen_tcp:close(C), + ok = file:delete(SFile), + ok. + +t_local_fdopen(_Config) -> + SFile = local_filename(server), + SAddr = {local,bin_filename(SFile)}, + _ = file:delete(SFile), + %% + L = ok(gen_tcp:listen(0, [{ifaddr,SAddr},{active,false}])), + C0 = ok(gen_tcp:connect(SAddr, 0, [{active,false}])), + Fd = ok(prim_inet:getfd(C0)), + ok = prim_inet:ignorefd(C0, true), + C = ok(gen_tcp:fdopen(Fd, [local])), + S = ok(gen_tcp:accept(L)), + SAddr = ok(inet:sockname(L)), + {error,enotconn} = inet:peername(L), + local_handshake(S, SAddr, C, {local,<<>>}), + ok = gen_tcp:close(L), + ok = gen_tcp:close(S), + ok = gen_tcp:close(C), + ok = gen_tcp:close(C0), + ok = file:delete(SFile), + ok. + +t_local_fdopen_listen(_Config) -> + SFile = local_filename(server), + SAddr = {local,bin_filename(SFile)}, + _ = file:delete(SFile), + L0 = ok(gen_tcp:listen(0, [{ifaddr,SAddr},{active,false}])), + Fd = ok(prim_inet:getfd(L0)), + L = ok(gen_tcp:listen(0, [{fd,Fd},local,{active,false}])), + C = ok(gen_tcp:connect(SAddr, 0, [{active,false}])), + S = ok(gen_tcp:accept(L)), + SAddr = ok(inet:sockname(L)), + {error,enotconn} = inet:peername(L), + local_handshake(S, SAddr, C, {local,<<>>}), + ok = gen_tcp:close(L), + ok = gen_tcp:close(L0), + ok = gen_tcp:close(S), + ok = gen_tcp:close(C), + ok = file:delete(SFile), + ok. + +t_local_fdopen_listen_unbound(_Config) -> + SFile = local_filename(server), + SAddr = {local,bin_filename(SFile)}, + _ = file:delete(SFile), + P = ok(prim_inet:open(tcp, local, stream)), + Fd = ok(prim_inet:getfd(P)), + L = + ok(gen_tcp:listen( + 0, [{fd,Fd},{ifaddr,SAddr},{active,false}])), + C = ok(gen_tcp:connect(SAddr, 0, [{active,false}])), + S = ok(gen_tcp:accept(L)), + SAddr = ok(inet:sockname(L)), + {error,enotconn} = inet:peername(L), + local_handshake(S, SAddr, C, {local,<<>>}), + ok = gen_tcp:close(L), + ok = gen_tcp:close(P), + ok = gen_tcp:close(S), + ok = gen_tcp:close(C), + ok = file:delete(SFile), + ok. + +t_local_fdopen_connect(_Config) -> + SFile = local_filename(server), + SAddr = {local,bin_filename(SFile)}, + CFile = local_filename(client), + CAddr = {local,bin_filename(CFile)}, + _ = file:delete(SFile), + _ = file:delete(CFile), + L = ok(gen_tcp:listen(0, [{ifaddr,SAddr},{active,false}])), + P = ok(prim_inet:open(tcp, local, stream)), + Fd = ok(prim_inet:getfd(P)), + C = + ok(gen_tcp:connect( + SAddr, 0, [{fd,Fd},{ifaddr,CAddr},{active,false}])), + S = ok(gen_tcp:accept(L)), + SAddr = ok(inet:sockname(L)), + {error,enotconn} = inet:peername(L), + local_handshake(S, SAddr, C, CAddr), + ok = gen_tcp:close(L), + ok = gen_tcp:close(S), + ok = gen_tcp:close(C), + ok = gen_tcp:close(P), + ok = file:delete(SFile), + ok. + +t_local_fdopen_connect_unbound(_Config) -> + SFile = local_filename(server), + SAddr = {local,bin_filename(SFile)}, + _ = file:delete(SFile), + L = ok(gen_tcp:listen(0, [{ifaddr,SAddr},{active,false}])), + P = ok(prim_inet:open(tcp, local, stream)), + Fd = ok(prim_inet:getfd(P)), + C = ok(gen_tcp:connect(SAddr, 0, [{fd,Fd},{active,false}])), + S = ok(gen_tcp:accept(L)), + SAddr = ok(inet:sockname(L)), + {error,enotconn} = inet:peername(L), + local_handshake(S, SAddr, C, {local,<<>>}), + ok = gen_tcp:close(L), + ok = gen_tcp:close(S), + ok = gen_tcp:close(C), + ok = gen_tcp:close(P), + ok = file:delete(SFile), + ok. + +t_local_abstract(_Config) -> + case os:type() of + {unix,linux} -> + AbstAddr = {local,<<>>}, + L = + ok(gen_tcp:listen( + 0, [{ifaddr,AbstAddr},{active,false}])), + {local,_} = SAddr = ok(inet:sockname(L)), + C = + ok(gen_tcp:connect( + SAddr, 0, [{ifaddr,AbstAddr},{active,false}])), + {local,_} = CAddr = ok(inet:sockname(C)), + S = ok(gen_tcp:accept(L)), + {error,enotconn} = inet:peername(L), + local_handshake(S, SAddr, C, CAddr), + ok = gen_tcp:close(L), + ok = gen_tcp:close(S), + ok = gen_tcp:close(C), + ok; + _ -> + {skip,"AF_LOCAL Abstract Addresses only supported on Linux"} + end. + + +local_handshake(S, SAddr, C, CAddr) -> + SData = "9876543210", + CData = "0123456789", + SAddr = ok(inet:sockname(S)), + CAddr = ok(inet:sockname(C)), + CAddr = ok(inet:peername(S)), + SAddr = ok(inet:peername(C)), + ok = gen_tcp:send(C, CData), + ok = gen_tcp:send(S, SData), + CData = ok(gen_tcp:recv(S, length(CData))), + SData = ok(gen_tcp:recv(C, length(SData))), + ok. + +t_accept_inet6_tclass(Config) when is_list(Config) -> + TClassOpt = {tclass,8#56 bsl 2}, % Expedited forwarding + Loopback = {0,0,0,0,0,0,0,1}, + case gen_tcp:listen(0, [inet6, {ip, Loopback}, TClassOpt]) of + {ok,L} -> + LPort = ok(inet:port(L)), + Sa = ok(gen_tcp:connect(Loopback, LPort, [])), + Sb = ok(gen_tcp:accept(L)), + [TClassOpt] = ok(inet:getopts(Sb, [tclass])), + ok = gen_tcp:close(Sb), + ok = gen_tcp:close(Sa), + ok = gen_tcp:close(L), + ok; + {error,_} -> + {skip,"IPv6 TCLASS not supported"} + end. %%% Utilities @@ -279,13 +548,13 @@ implicit_inet6(S, Addr) -> timeout({M,F,A}, Lower, Upper) -> case test_server:timecall(M, F, A) of {Time, Result} when Time < Lower -> - test_server:fail({too_short_time, Time, Result}); + ct:fail({too_short_time, Time, Result}); {Time, Result} when Time > Upper -> - test_server:fail({too_long_time, Time, Result}); + ct:fail({too_long_time, Time, Result}); {_, {error, timeout}} -> ok; {_, Result} -> - test_server:fail({unexpected_result, Result}) + ct:fail({unexpected_result, Result}) end. connect_timeout({M,F,A}, Lower, Upper) -> @@ -300,28 +569,28 @@ connect_timeout({M,F,A}, Lower, Upper) -> Pinfo = erlang:port_info(Socket), Db = inet_db:lookup_socket(Socket), Peer = inet:peername(Socket), - test_server:fail({too_short_time, Time, - [Result,Pinfo,Db,Peer]}); + ct:fail({too_short_time, Time, + [Result,Pinfo,Db,Peer]}); _ -> - test_server:fail({too_short_time, Time, Result}) + ct:fail({too_short_time, Time, Result}) end; {Time, Result} when Time > Upper -> - test_server:fail({too_long_time, Time, Result}); + ct:fail({too_long_time, Time, Result}); {_, {error, timeout}} -> ok; {_, Result} -> - test_server:fail({unexpected_result, Result}) + ct:fail({unexpected_result, Result}) end. %% Try to obtain an unused IP address in the local network. unused_ip() -> - ?line {ok, Host} = inet:gethostname(), - ?line {ok, Hent} = inet:gethostbyname(Host), - ?line #hostent{h_addr_list=[{A, B, C, _D}|_]} = Hent, + {ok, Host} = inet:gethostname(), + {ok, Hent} = inet:gethostbyname(Host), + #hostent{h_addr_list=[{A, B, C, _D}|_]} = Hent, %% Note: In our net, addresses below 16 are reserved for routers and %% other strange creatures. - ?line IP = unused_ip(A, B, C, 16), + IP = unused_ip(A, B, C, 16), io:format("we = ~p, unused_ip = ~p~n", [Hent, IP]), IP. @@ -332,8 +601,42 @@ unused_ip(A, B, C, D) -> {error, _} -> {ok, {A, B, C, D}} end. -ok({ok,V}) -> V. +ok({ok,V}) -> V; +ok(NotOk) -> + try throw(not_ok) + catch + throw:Thrown:Stacktrace -> + erlang:raise( + error, {Thrown, NotOk}, tl(Stacktrace)) + end. +get_localaddr() -> + get_localaddr(["localhost", "localhost6", "ip6-localhost"]). + +get_localaddr([]) -> + {error, localaddr_not_found}; +get_localaddr([Localhost|Ls]) -> + case inet:getaddr(Localhost, inet6) of + {ok, LocalAddr} -> + io:format("~s ~p~n", [Localhost, LocalAddr]), + {ok, LocalAddr}; + _ -> + get_localaddr(Ls) + end. getsockfd() -> undefined. closesockfd(_FD) -> undefined. + +local_filename(Tag) -> + "/tmp/" ?MODULE_STRING "_" ++ os:getpid() ++ "_" ++ atom_to_list(Tag). + +bin_filename(String) -> + unicode:characters_to_binary(String, file:native_name_encoding()). + +delete_local_filenames() -> + _ = + [file:delete(F) || + F <- + filelib:wildcard( + "/tmp/" ?MODULE_STRING "_" ++ os:getpid() ++ "_*")], + ok. diff --git a/lib/kernel/test/gen_tcp_api_SUITE_data/gen_tcp_api_SUITE.c b/lib/kernel/test/gen_tcp_api_SUITE_data/gen_tcp_api_SUITE.c index d774767624..b91dca61d4 100644 --- a/lib/kernel/test/gen_tcp_api_SUITE_data/gen_tcp_api_SUITE.c +++ b/lib/kernel/test/gen_tcp_api_SUITE_data/gen_tcp_api_SUITE.c @@ -1,22 +1,23 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2009-2013. All Rights Reserved. + * Copyright Ericsson AB 2009-2016. 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/. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * 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. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * %CopyrightEnd% */ -#include "erl_nif.h" +#include <erl_nif.h> #include <stdio.h> #include <string.h> diff --git a/lib/kernel/test/gen_tcp_echo_SUITE.erl b/lib/kernel/test/gen_tcp_echo_SUITE.erl index 9bc66dbae0..57525f8015 100644 --- a/lib/kernel/test/gen_tcp_echo_SUITE.erl +++ b/lib/kernel/test/gen_tcp_echo_SUITE.erl @@ -1,24 +1,25 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2013. All Rights Reserved. +%% Copyright Ericsson AB 1997-2016. 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% -module(gen_tcp_echo_SUITE). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). %%-compile(export_all). @@ -33,7 +34,9 @@ -define(TPKT_VRSN, 3). -define(LINE_LENGTH, 1023). % (default value of gen_tcp option 'recbuf') - 1 -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap,{minutes,5}}]. all() -> [active_echo, passive_echo, active_once_echo, @@ -58,94 +61,75 @@ end_per_group(_GroupName, Config) -> init_per_testcase(_Func, Config) -> - Dog = test_server:timetrap(test_server:minutes(5)), - [{watchdog, Dog}|Config]. -end_per_testcase(_Func, Config) -> - Dog = ?config(watchdog, Config), - test_server:timetrap_cancel(Dog). - -active_echo(doc) -> - ["Test sending packets of various sizes and various packet types ", - "to the echo port and receiving them again (socket in active mode)."]; -active_echo(suite) -> []; + Config. + +end_per_testcase(_Func, _Config) -> + ok. + +%% Test sending packets of various sizes and various packet types +%% to the echo port and receiving them again (socket in active mode). active_echo(Config) when is_list(Config) -> - ?line echo_test([], fun active_echo/4, [{echo, fun echo_server/0}]). + echo_test([], fun active_echo/4, [{echo, fun echo_server/0}]). -passive_echo(doc) -> - ["Test sending packets of various sizes and various packet types ", - "to the echo port and receiving them again (socket in passive mode)."]; -passive_echo(suite) -> []; +%% Test sending packets of various sizes and various packet types +%% to the echo port and receiving them again (socket in passive mode). passive_echo(Config) when is_list(Config) -> - ?line echo_test([{active, false}], fun passive_echo/4, - [{echo, fun echo_server/0}]). + echo_test([{active, false}], fun passive_echo/4, + [{echo, fun echo_server/0}]). -active_once_echo(doc) -> - ["Test sending packets of various sizes and various packet types ", - "to the echo port and receiving them again (socket in active once mode)."]; -active_once_echo(suite) -> []; +%% Test sending packets of various sizes and various packet types +%% to the echo port and receiving them again (socket in active once mode). active_once_echo(Config) when is_list(Config) -> - ?line echo_test([{active, once}], fun active_once_echo/4, - [{echo, fun echo_server/0}]). - -slow_active_echo(doc) -> - ["Test sending packets of various sizes and various packet types ", - "to the echo port and receiving them again (socket in active mode). ", - "The echo server is a special one that delays between every character."]; -slow_active_echo(suite) -> []; + echo_test([{active, once}], fun active_once_echo/4, + [{echo, fun echo_server/0}]). + +%% Test sending packets of various sizes and various packet types +%% to the echo port and receiving them again (socket in active mode). +%% The echo server is a special one that delays between every character. slow_active_echo(Config) when is_list(Config) -> - ?line echo_test([], fun active_echo/4, - [slow_echo, {echo, fun slow_echo_server/0}]). - -slow_passive_echo(doc) -> - ["Test sending packets of various sizes and various packet types ", - "to an echo server and receiving them again (socket in passive mode).", - "The echo server is a special one that delays between every character."]; -slow_passive_echo(suite) -> []; + echo_test([], fun active_echo/4, + [slow_echo, {echo, fun slow_echo_server/0}]). + +%% Test sending packets of various sizes and various packet types +%% to an echo server and receiving them again (socket in passive mode). +%% The echo server is a special one that delays between every character. slow_passive_echo(Config) when is_list(Config) -> - ?line echo_test([{active, false}], fun passive_echo/4, - [slow_echo, {echo, fun slow_echo_server/0}]). - -limit_active_echo(doc) -> - ["Test sending packets of various sizes and various packet types ", - "to the echo port and receiving them again (socket in active mode) " - "with packet_size limitation."]; -limit_active_echo(suite) -> []; + echo_test([{active, false}], fun passive_echo/4, + [slow_echo, {echo, fun slow_echo_server/0}]). + +%% Test sending packets of various sizes and various packet types +%% to the echo port and receiving them again (socket in active mode) +%% with packet_size limitation. limit_active_echo(Config) when is_list(Config) -> - ?line echo_test([{packet_size, 10}], - fun active_echo/4, - [{packet_size, 10}, {echo, fun echo_server/0}]). - -limit_passive_echo(doc) -> - ["Test sending packets of various sizes and various packet types ", - "to the echo port and receiving them again (socket in passive mode) ", - "with packet_size limitation."]; -limit_passive_echo(suite) -> []; + echo_test([{packet_size, 10}], + fun active_echo/4, + [{packet_size, 10}, {echo, fun echo_server/0}]). + +%% Test sending packets of various sizes and various packet types +%% to the echo port and receiving them again (socket in passive mode) +%% with packet_size limitation. limit_passive_echo(Config) when is_list(Config) -> - ?line echo_test([{packet_size, 10},{active, false}], - fun passive_echo/4, - [{packet_size, 10}, {echo, fun echo_server/0}]). - -large_limit_active_echo(doc) -> - ["Test sending packets of various sizes and various packet types ", - "to the echo port and receiving them again (socket in active mode) " - "with large packet_size limitation."]; -large_limit_active_echo(suite) -> []; + echo_test([{packet_size, 10},{active, false}], + fun passive_echo/4, + [{packet_size, 10}, {echo, fun echo_server/0}]). + +%% Test sending packets of various sizes and various packet types +%% to the echo port and receiving them again (socket in active mode) +%% with large packet_size limitation. large_limit_active_echo(Config) when is_list(Config) -> - ?line echo_test([{packet_size, 10}], - fun active_echo/4, - [{packet_size, (1 bsl 32)-1}, - {echo, fun echo_server/0}]). - -large_limit_passive_echo(doc) -> - ["Test sending packets of various sizes and various packet types ", - "to the echo port and receiving them again (socket in passive mode) ", - "with large packet_size limitation."]; -large_limit_passive_echo(suite) -> []; + echo_test([{packet_size, 10}], + fun active_echo/4, + [{packet_size, (1 bsl 32)-1}, + {echo, fun echo_server/0}]). + +%% Test sending packets of various sizes and various packet types +%% to the echo port and receiving them again (socket in passive mode) +%% with large packet_size limitation. large_limit_passive_echo(Config) when is_list(Config) -> - ?line echo_test([{packet_size, 10},{active, false}], - fun passive_echo/4, - [{packet_size, (1 bsl 32) -1}, - {echo, fun echo_server/0}]). + echo_test([{packet_size, 10},{active, false}], + fun passive_echo/4, + [{packet_size, (1 bsl 32) -1}, + {echo, fun echo_server/0}]). echo_test(SockOpts, EchoFun, Config0) -> echo_test_1(SockOpts, EchoFun, Config0), @@ -153,53 +137,53 @@ echo_test(SockOpts, EchoFun, Config0) -> echo_test_1([{delay_send,true}|SockOpts], EchoFun, Config0). echo_test_1(SockOpts, EchoFun, Config0) -> - ?line EchoSrvFun = ?config(echo, Config0), - ?line {ok, EchoPort} = EchoSrvFun(), - ?line Config = [{echo_port, EchoPort}|Config0], - - ?line echo_packet([{packet, 1}|SockOpts], EchoFun, Config), - ?line echo_packet([{packet, 2}|SockOpts], EchoFun, Config), - ?line echo_packet([{packet, 4}|SockOpts], EchoFun, Config), - ?line echo_packet([{packet, sunrm}|SockOpts], EchoFun, Config), - ?line echo_packet([{packet, cdr}|SockOpts], EchoFun, - [{type, {cdr, big}}|Config]), - ?line echo_packet([{packet, cdr}|SockOpts], EchoFun, - [{type, {cdr, little}}|Config]), - ?line case lists:keymember(packet_size, 1, SockOpts) of - false -> - % This is cheating, we should test that packet_size - % also works for line and http. - echo_packet([{packet, line}|SockOpts], EchoFun, Config), - echo_packet([{packet, http}|SockOpts], EchoFun, Config), - echo_packet([{packet, http_bin}|SockOpts], EchoFun, Config); - - true -> ok - end, - ?line echo_packet([{packet, tpkt}|SockOpts], EchoFun, Config), - - ?line ShortTag = [16#E0], - ?line LongTag = [16#1F, 16#83, 16#27], - ?line echo_packet([{packet, asn1}|SockOpts], EchoFun, - [{type, {asn1, short, ShortTag}}|Config]), - ?line echo_packet([{packet, asn1}|SockOpts], EchoFun, - [{type, {asn1, long, ShortTag}}|Config]), - ?line echo_packet([{packet, asn1}|SockOpts], EchoFun, - [{type, {asn1, short, LongTag}}|Config]), - ?line echo_packet([{packet, asn1}|SockOpts], EchoFun, - [{type, {asn1, long, LongTag}}|Config]), + EchoSrvFun = proplists:get_value(echo, Config0), + {ok, EchoPort} = EchoSrvFun(), + Config = [{echo_port, EchoPort}|Config0], + + echo_packet([{packet, 1}|SockOpts], EchoFun, Config), + echo_packet([{packet, 2}|SockOpts], EchoFun, Config), + echo_packet([{packet, 4}|SockOpts], EchoFun, Config), + echo_packet([{packet, sunrm}|SockOpts], EchoFun, Config), + echo_packet([{packet, cdr}|SockOpts], EchoFun, + [{type, {cdr, big}}|Config]), + echo_packet([{packet, cdr}|SockOpts], EchoFun, + [{type, {cdr, little}}|Config]), + case lists:keymember(packet_size, 1, SockOpts) of + false -> + %% This is cheating, we should test that packet_size + %% also works for line and http. + echo_packet([{packet, line}|SockOpts], EchoFun, Config), + echo_packet([{packet, http}|SockOpts], EchoFun, Config), + echo_packet([{packet, http_bin}|SockOpts], EchoFun, Config); + + true -> ok + end, + echo_packet([{packet, tpkt}|SockOpts], EchoFun, Config), + + ShortTag = [16#E0], + LongTag = [16#1F, 16#83, 16#27], + echo_packet([{packet, asn1}|SockOpts], EchoFun, + [{type, {asn1, short, ShortTag}}|Config]), + echo_packet([{packet, asn1}|SockOpts], EchoFun, + [{type, {asn1, long, ShortTag}}|Config]), + echo_packet([{packet, asn1}|SockOpts], EchoFun, + [{type, {asn1, short, LongTag}}|Config]), + echo_packet([{packet, asn1}|SockOpts], EchoFun, + [{type, {asn1, long, LongTag}}|Config]), ok. echo_packet(SockOpts, EchoFun, Opts) -> Type = case lists:keysearch(type, 1, Opts) of - {value, {type, T}} -> - T; - _ -> - {value, {packet, T}} = lists:keysearch(packet, 1, SockOpts), - T - end, + {value, {type, T}} -> + T; + _ -> + {value, {packet, T}} = lists:keysearch(packet, 1, SockOpts), + T + end, %% Connect to the echo server. - EchoPort = ?config(echo_port, Opts), + EchoPort = proplists:get_value(echo_port, Opts), {ok, Echo} = gen_tcp:connect(localhost, EchoPort, SockOpts), SlowEcho = lists:member(slow_echo, Opts), @@ -222,83 +206,78 @@ echo_packet_http(Echo, Type, EchoFun) -> EchoFun(Echo, Type, P2, http_reply(P2, Type)). echo_packet0(Echo, Type, EchoFun, SlowEcho, Opts) -> - ?line PacketSize = + PacketSize = case lists:keysearch(packet_size, 1, Opts) of {value,{packet_size,Sz}} when Sz < 10 -> Sz; {value,{packet_size,_}} -> 10; false -> 0 end, %% Echo small packets first. - ?line echo_packet1(Echo, Type, EchoFun, 0), - ?line echo_packet1(Echo, Type, EchoFun, 1), - ?line echo_packet1(Echo, Type, EchoFun, 2), - ?line echo_packet1(Echo, Type, EchoFun, 3), - ?line echo_packet1(Echo, Type, EchoFun, 4), - ?line echo_packet1(Echo, Type, EchoFun, 7), + echo_packet1(Echo, Type, EchoFun, 0), + echo_packet1(Echo, Type, EchoFun, 1), + echo_packet1(Echo, Type, EchoFun, 2), + echo_packet1(Echo, Type, EchoFun, 3), + echo_packet1(Echo, Type, EchoFun, 4), + echo_packet1(Echo, Type, EchoFun, 7), if PacketSize =/= 0 -> - ?line echo_packet1(Echo, Type, EchoFun, - {PacketSize-1, PacketSize}), - ?line echo_packet1(Echo, Type, EchoFun, - {PacketSize, PacketSize}), - ?line echo_packet1(Echo, Type, EchoFun, - {PacketSize+1, PacketSize}); + echo_packet1(Echo, Type, EchoFun, + {PacketSize-1, PacketSize}), + echo_packet1(Echo, Type, EchoFun, + {PacketSize, PacketSize}), + echo_packet1(Echo, Type, EchoFun, + {PacketSize+1, PacketSize}); not SlowEcho -> % Go on with bigger packets if not slow echo server. - ?line echo_packet1(Echo, Type, EchoFun, 10), - ?line echo_packet1(Echo, Type, EchoFun, 13), - ?line echo_packet1(Echo, Type, EchoFun, 126), - ?line echo_packet1(Echo, Type, EchoFun, 127), - ?line echo_packet1(Echo, Type, EchoFun, 128), - ?line echo_packet1(Echo, Type, EchoFun, 255), - ?line echo_packet1(Echo, Type, EchoFun, 256), - ?line echo_packet1(Echo, Type, EchoFun, 1023), - ?line echo_packet1(Echo, Type, EchoFun, 3747), - ?line echo_packet1(Echo, Type, EchoFun, 32767), - ?line echo_packet1(Echo, Type, EchoFun, 32768), - ?line echo_packet1(Echo, Type, EchoFun, 65531), - ?line echo_packet1(Echo, Type, EchoFun, 65535), - ?line echo_packet1(Echo, Type, EchoFun, 65536), - ?line echo_packet1(Echo, Type, EchoFun, 70000), - ?line echo_packet1(Echo, Type, EchoFun, infinite); + echo_packet1(Echo, Type, EchoFun, 10), + echo_packet1(Echo, Type, EchoFun, 13), + echo_packet1(Echo, Type, EchoFun, 126), + echo_packet1(Echo, Type, EchoFun, 127), + echo_packet1(Echo, Type, EchoFun, 128), + echo_packet1(Echo, Type, EchoFun, 255), + echo_packet1(Echo, Type, EchoFun, 256), + echo_packet1(Echo, Type, EchoFun, 1023), + echo_packet1(Echo, Type, EchoFun, 3747), + echo_packet1(Echo, Type, EchoFun, 32767), + echo_packet1(Echo, Type, EchoFun, 32768), + echo_packet1(Echo, Type, EchoFun, 65531), + echo_packet1(Echo, Type, EchoFun, 65535), + echo_packet1(Echo, Type, EchoFun, 65536), + echo_packet1(Echo, Type, EchoFun, 70000), + echo_packet1(Echo, Type, EchoFun, infinite); true -> ok end, - ?line gen_tcp:close(Echo), + gen_tcp:close(Echo), ok. echo_packet1(EchoSock, Type, EchoFun, Size) -> - ?line case packet(Size, Type) of - false -> - ok; - Packet -> - ?line io:format("Type ~p, size ~p, time ~p", - [Type, Size, time()]), - ?line - case EchoFun(EchoSock, Type, Packet, [Packet]) of - ok -> - ?line - case Size of - {N, Max} when N > Max -> - ?line - test_server:fail( - {packet_through, {N, Max}}); - _ -> ok - end; - {error, emsgsize} -> - ?line - case Size of - {N, Max} when N > Max -> - io:format(" Blocked!"); - _ -> - ?line - test_server:fail( - {packet_blocked, Size}) - end; - Error -> - ?line test_server:fail(Error) - end - end. + case packet(Size, Type) of + false -> + ok; + Packet -> + io:format("Type ~p, size ~p, time ~p", + [Type, Size, time()]), + case EchoFun(EchoSock, Type, Packet, [Packet]) of + ok -> + case Size of + {N, Max} when N > Max -> + ct:fail( + {packet_through, {N, Max}}); + _ -> ok + end; + {error, emsgsize} -> + case Size of + {N, Max} when N > Max -> + io:format(" Blocked!"); + _ -> + ct:fail( + {packet_blocked, Size}) + end; + Error -> + ct:fail(Error) + end + end. active_echo(Sock, Type, Packet, PacketEchos) -> - ?line ok = gen_tcp:send(Sock, Packet), + ok = gen_tcp:send(Sock, Packet), active_recv(Sock, Type, PacketEchos). active_recv(_, _, []) -> @@ -309,21 +288,21 @@ active_recv(Sock, Type, [PacketEcho|Tail]) -> http_bin -> http; _ -> tcp end, - ?line receive Recv->Recv end, + receive Recv->Recv end, %%io:format("Active received: ~p\n",[Recv]), - ?line case Recv of - {Tag, Sock, PacketEcho} -> - active_recv(Sock, Type, Tail); - {Tag, Sock, Bad} -> - ?line test_server:fail({wrong_data, Bad, expected, PacketEcho}); - {tcp_error, Sock, Reason} -> - {error, Reason}; - Other -> - ?line test_server:fail({unexpected_message, Other, Tag}) - end. + case Recv of + {Tag, Sock, PacketEcho} -> + active_recv(Sock, Type, Tail); + {Tag, Sock, Bad} -> + ct:fail({wrong_data, Bad, expected, PacketEcho}); + {tcp_error, Sock, Reason} -> + {error, Reason}; + Other -> + ct:fail({unexpected_message, Other, Tag}) + end. passive_echo(Sock, _Type, Packet, PacketEchos) -> - ?line ok = gen_tcp:send(Sock, Packet), + ok = gen_tcp:send(Sock, Packet), passive_recv(Sock, PacketEchos). passive_recv(_, []) -> @@ -331,22 +310,22 @@ passive_recv(_, []) -> passive_recv(Sock, [PacketEcho | Tail]) -> Recv = gen_tcp:recv(Sock, 0), %%io:format("Passive received: ~p\n",[Recv]), - ?line case Recv of - {ok, PacketEcho} -> - passive_recv(Sock, Tail); - {ok, Bad} -> - io:format("Expected: ~p\nGot: ~p\n",[PacketEcho,Bad]), - ?line test_server:fail({wrong_data, Bad}); - {error,PacketEcho} -> - passive_recv(Sock, Tail); % expected error - {error, _}=Error -> - Error; - Other -> - ?line test_server:fail({unexpected_message, Other}) - end. + case Recv of + {ok, PacketEcho} -> + passive_recv(Sock, Tail); + {ok, Bad} -> + io:format("Expected: ~p\nGot: ~p\n",[PacketEcho,Bad]), + ct:fail({wrong_data, Bad}); + {error,PacketEcho} -> + passive_recv(Sock, Tail); % expected error + {error, _}=Error -> + Error; + Other -> + ct:fail({unexpected_message, Other}) + end. active_once_echo(Sock, Type, Packet, PacketEchos) -> - ?line ok = gen_tcp:send(Sock, Packet), + ok = gen_tcp:send(Sock, Packet), active_once_recv(Sock, Type, PacketEchos). active_once_recv(_, _, []) -> @@ -357,17 +336,17 @@ active_once_recv(Sock, Type, [PacketEcho | Tail]) -> http_bin -> http; _ -> tcp end, - ?line receive - {Tag, Sock, PacketEcho} -> - inet:setopts(Sock, [{active, once}]), - active_once_recv(Sock, Type, Tail); - {Tag, Sock, Bad} -> - ?line test_server:fail({wrong_data, Bad}); - {tcp_error, Sock, Reason} -> - {error, Reason}; - Other -> - ?line test_server:fail({unexpected_message, Other, expected, {Tag, Sock, PacketEcho}}) - end. + receive + {Tag, Sock, PacketEcho} -> + inet:setopts(Sock, [{active, once}]), + active_once_recv(Sock, Type, Tail); + {Tag, Sock, Bad} -> + ct:fail({wrong_data, Bad}); + {tcp_error, Sock, Reason} -> + {error, Reason}; + Other -> + ct:fail({unexpected_message, Other, expected, {Tag, Sock, PacketEcho}}) + end. %%% Building of random packets. @@ -441,14 +420,7 @@ random_char(Chars) -> lists:nth(uniform(length(Chars)), Chars). uniform(N) -> - case get(random_seed) of - undefined -> - {X, Y, Z} = time(), - random:seed(X, Y, Z); - _ -> - ok - end, - random:uniform(N). + rand:uniform(N). put_int32(X, big, List) -> [ (X bsr 24) band 16#ff, @@ -457,9 +429,9 @@ put_int32(X, big, List) -> (X) band 16#ff | List ]; put_int32(X, little, List) -> [ (X) band 16#ff, - (X bsr 8) band 16#ff, - (X bsr 16) band 16#ff, - (X bsr 24) band 16#ff | List]. + (X bsr 8) band 16#ff, + (X bsr 16) band 16#ff, + (X bsr 24) band 16#ff | List]. put_int16(X, ByteOrder) -> put_int16(X, ByteOrder, []). @@ -469,16 +441,16 @@ put_int16(X, big, List) -> (X) band 16#ff | List ]; put_int16(X, little, List) -> [ (X) band 16#ff, - (X bsr 8) band 16#ff | List ]. + (X bsr 8) band 16#ff | List ]. %%% A normal echo server, for systems that don't have one. echo_server() -> Self = self(), - ?line spawn_link(fun() -> echo_server(Self) end), - ?line receive - {echo_port, Port} -> - {ok, Port} + spawn_link(fun() -> echo_server(Self) end), + receive + {echo_port, Port} -> + {ok, Port} end. echo_server(ReplyTo) -> @@ -511,11 +483,11 @@ echoer_loop(Sock) -> slow_echo_server() -> Self = self(), - ?line spawn_link(fun() -> slow_echo_server(Self) end), - ?line receive - {echo_port, Port} -> - {ok, Port} - end. + spawn_link(fun() -> slow_echo_server(Self) end), + receive + {echo_port, Port} -> + {ok, Port} + end. slow_echo_server(ReplyTo) -> {ok, S} = gen_tcp:listen(0, [{active, false}, {nodelay, true}]), @@ -551,17 +523,17 @@ slow_send(_, []) -> http_request(Uri) -> list_to_binary(["POST ", Uri, <<" HTTP/1.1\r\n" - "Connection: close\r\n" - "Host: localhost:8000\r\n" - "User-Agent: perl post\r\n" - "Content-Length: 4\r\n" - "Content-Type: text/xml; charset=utf-8\r\n" - "Other-Field: with some text\r\n" - "Multi-Line: Once upon a time in a land far far away,\r\n" - " there lived a princess imprisoned in the highest tower\r\n" - " of the most haunted castle.\r\n" - "Invalid line without a colon\r\n" - "\r\n">>]). + "Connection: close\r\n" + "Host: localhost:8000\r\n" + "User-Agent: perl post\r\n" + "Content-Length: 4\r\n" + "Content-Type: text/xml; charset=utf-8\r\n" + "Other-Field: with some text\r\n" + "Multi-Line: Once upon a time in a land far far away,\r\n" + " there lived a princess imprisoned in the highest tower\r\n" + " of the most haunted castle.\r\n" + "Invalid line without a colon\r\n" + "\r\n">>]). http_uri_variants() -> ["*", @@ -574,11 +546,11 @@ http_uri_variants() -> http_response() -> <<"HTTP/1.0 404 Object Not Found\r\n" - "Server: inets/4.7.16\r\n" - "Date: Fri, 04 Jul 2008 17:16:22 GMT\r\n" - "Content-Type: text/html\r\n" - "Content-Length: 207\r\n" - "\r\n">>. + "Server: inets/4.7.16\r\n" + "Date: Fri, 04 Jul 2008 17:16:22 GMT\r\n" + "Content-Type: text/html\r\n" + "Content-Length: 207\r\n" + "\r\n">>. http_reply(Bin, Type) -> {ok, Line, Rest} = erlang:decode_packet(Type,Bin,[]), @@ -595,7 +567,3 @@ http_reply(<<>>, Acc, _) -> http_reply(Bin, Acc, HType) -> {ok, Line, Rest} = erlang:decode_packet(HType,Bin,[]), http_reply(Rest, [Line | Acc], HType). - - - - diff --git a/lib/kernel/test/gen_tcp_misc_SUITE.erl b/lib/kernel/test/gen_tcp_misc_SUITE.erl index 4e4aeb67e2..358ca872f7 100644 --- a/lib/kernel/test/gen_tcp_misc_SUITE.erl +++ b/lib/kernel/test/gen_tcp_misc_SUITE.erl @@ -1,90 +1,73 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2014. All Rights Reserved. +%% Copyright Ericsson AB 1998-2018. 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/. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at %% -%% 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. +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% -module(gen_tcp_misc_SUITE). --include_lib("test_server/include/test_server.hrl"). - -%-compile(export_all). +-include_lib("common_test/include/ct.hrl"). -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, controlling_process/1, controlling_process_self/1, no_accept/1, close_with_pending_output/1, active_n/1, - data_before_close/1, iter_max_socks/1, get_status/1, + data_before_close/1, + iter_max_socks/0, iter_max_socks/1, + get_status/1, passive_sockets/1, accept_closed_by_other_process/1, init_per_testcase/2, end_per_testcase/2, otp_3924/1, otp_3924_sender/4, closed_socket/1, shutdown_active/1, shutdown_passive/1, shutdown_pending/1, + show_econnreset_active/1, show_econnreset_active_once/1, + show_econnreset_passive/1, econnreset_after_sync_send/1, + econnreset_after_async_send_active/1, + econnreset_after_async_send_active_once/1, + econnreset_after_async_send_passive/1, linger_zero/1, default_options/1, http_bad_packet/1, busy_send/1, busy_disconnect_passive/1, busy_disconnect_active/1, fill_sendq/1, partial_recv_and_close/1, partial_recv_and_close_2/1,partial_recv_and_close_3/1,so_priority/1, - % Accept tests + recvtos/1, recvttl/1, recvtosttl/1, recvtclass/1, + %% Accept tests primitive_accept/1,multi_accept_close_listen/1,accept_timeout/1, accept_timeouts_in_order/1,accept_timeouts_in_order2/1, - accept_timeouts_in_order3/1,accept_timeouts_mixed/1, + accept_timeouts_in_order3/1,accept_timeouts_in_order4/1, + accept_timeouts_in_order5/1,accept_timeouts_in_order6/1, + accept_timeouts_in_order7/1,accept_timeouts_mixed/1, killing_acceptor/1,killing_multi_acceptors/1,killing_multi_acceptors2/1, several_accepts_in_one_go/1, accept_system_limit/1, active_once_closed/1, send_timeout/1, send_timeout_active/1, - otp_7731/1, zombie_sockets/1, otp_7816/1, otp_8102/1, wrapping_oct/1, - otp_9389/1]). + otp_7731/1, zombie_sockets/1, otp_7816/1, otp_8102/1, + wrapping_oct/0, wrapping_oct/1, otp_9389/1, otp_13939/1]). %% Internal exports. -export([sender/3, not_owner/1, passive_sockets_server/2, priority_server/1, oct_acceptor/1, otp_7731_server/1, zombie_server/2, do_iter_max_socks/2]). -init_per_testcase(wrapping_oct, Config) when is_list(Config) -> - Dog = case os:type() of - {ose,_} -> - test_server:timetrap(test_server:minutes(20)); - _Else -> - test_server:timetrap(test_server:seconds(600)) - end, - [{watchdog, Dog}|Config]; -init_per_testcase(iter_max_socks, Config) when is_list(Config) -> - Dog = case os:type() of - {win32,_} -> - test_server:timetrap(test_server:minutes(30)); - _Else -> - test_server:timetrap(test_server:seconds(240)) - end, - [{watchdog, Dog}|Config]; -init_per_testcase(accept_system_limit, Config) when is_list(Config) -> - case os:type() of - {ose,_} -> - {skip,"Skip in OSE"}; - _ -> - Dog = test_server:timetrap(test_server:seconds(240)), - [{watchdog,Dog}|Config] - end; -init_per_testcase(wrapping_oct, Config) when is_list(Config) -> - Dog = test_server:timetrap(test_server:seconds(600)), - [{watchdog, Dog}|Config]; -init_per_testcase(_Func, Config) when is_list(Config) -> - Dog = test_server:timetrap(test_server:seconds(240)), - [{watchdog, Dog}|Config]. -end_per_testcase(_Func, Config) -> - Dog = ?config(watchdog, Config), - test_server:timetrap_cancel(Dog). - -suite() -> [{ct_hooks,[ts_install_cth]}]. +init_per_testcase(_Func, Config) -> + Config. + +end_per_testcase(_Func, _Config) -> + ok. + +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap,{minutes,4}}]. all() -> [controlling_process, controlling_process_self, no_accept, @@ -92,14 +75,22 @@ all() -> iter_max_socks, passive_sockets, active_n, accept_closed_by_other_process, otp_3924, closed_socket, shutdown_active, shutdown_passive, shutdown_pending, + show_econnreset_active, show_econnreset_active_once, + show_econnreset_passive, econnreset_after_sync_send, + econnreset_after_async_send_active, + econnreset_after_async_send_active_once, + econnreset_after_async_send_passive, linger_zero, default_options, http_bad_packet, busy_send, busy_disconnect_passive, busy_disconnect_active, fill_sendq, partial_recv_and_close, partial_recv_and_close_2, partial_recv_and_close_3, - so_priority, primitive_accept, + so_priority, recvtos, recvttl, recvtosttl, + recvtclass, primitive_accept, multi_accept_close_listen, accept_timeout, accept_timeouts_in_order, accept_timeouts_in_order2, - accept_timeouts_in_order3, accept_timeouts_mixed, + accept_timeouts_in_order3, accept_timeouts_in_order4, + accept_timeouts_in_order5, accept_timeouts_in_order6, + accept_timeouts_in_order7, accept_timeouts_mixed, killing_acceptor, killing_multi_acceptors, killing_multi_acceptors2, several_accepts_in_one_go, accept_system_limit, active_once_closed, send_timeout, send_timeout_active, otp_7731, @@ -121,78 +112,77 @@ init_per_group(_GroupName, Config) -> end_per_group(_GroupName, Config) -> Config. +-define(UNIQ_NODE_NAME, + list_to_atom(?MODULE_STRING ++ "__" ++ + atom_to_list(?FUNCTION_NAME) ++ "_" ++ + integer_to_list(erlang:unique_integer([positive])))). - -default_options(doc) -> - ["Tests kernel application variables inet_default_listen_options and " - "inet_default_connect_options"]; -default_options(suite) -> - []; +%% Tests kernel application variables inet_default_listen_options and +%% inet_default_connect_options. default_options(Config) when is_list(Config) -> %% First check the delay_send option - ?line {true,true,true}=do_delay_send_1(), - ?line {false,false,false}=do_delay_send_2(), - ?line {true,false,false}=do_delay_send_3(), - ?line {false,false,false}=do_delay_send_4(), - ?line {false,false,false}=do_delay_send_5(), - ?line {false,true,true}=do_delay_send_6(), + {true,true,true}=do_delay_send_1(), + {false,false,false}=do_delay_send_2(), + {true,false,false}=do_delay_send_3(), + {false,false,false}=do_delay_send_4(), + {false,false,false}=do_delay_send_5(), + {false,true,true}=do_delay_send_6(), %% Now lets start some nodes with different combinations of options: - ?line {true,true,true} = do_delay_on_other_node("", - fun do_delay_send_1/0), - ?line {true,false,false} = + {true,true,true} = do_delay_on_other_node("", fun do_delay_send_1/0), + {true,false,false} = do_delay_on_other_node("-kernel inet_default_connect_options " "\"[{delay_send,true}]\"", fun do_delay_send_2/0), - ?line {false,true,true} = + {false,true,true} = do_delay_on_other_node("-kernel inet_default_listen_options " "\"[{delay_send,true}]\"", fun do_delay_send_2/0), - ?line {true,true,true} = + {true,true,true} = do_delay_on_other_node("-kernel inet_default_listen_options " "\"[{delay_send,true}]\"", fun do_delay_send_3/0), - ?line {true,true,true} = + {true,true,true} = do_delay_on_other_node("-kernel inet_default_connect_options " "\"[{delay_send,true}]\"", fun do_delay_send_6/0), - ?line {false,false,false} = + {false,false,false} = do_delay_on_other_node("-kernel inet_default_connect_options " "\"[{delay_send,true}]\"", fun do_delay_send_5/0), - ?line {false,true,true} = + {false,true,true} = do_delay_on_other_node("-kernel inet_default_connect_options " "\"[{delay_send,true}]\" " "-kernel inet_default_listen_options " "\"[{delay_send,true}]\"", fun do_delay_send_5/0), - ?line {true,false,false} = + {true,false,false} = do_delay_on_other_node("-kernel inet_default_connect_options " "\"[{delay_send,true}]\" " "-kernel inet_default_listen_options " "\"[{delay_send,true}]\"", fun do_delay_send_4/0), - ?line {true,true,true} = + {true,true,true} = do_delay_on_other_node("-kernel inet_default_connect_options " "\"{delay_send,true}\" " "-kernel inet_default_listen_options " "\"{delay_send,true}\"", fun do_delay_send_2/0), %% Active is to dangerous and is supressed - ?line {true,true,true} = + {true,true,true} = do_delay_on_other_node("-kernel inet_default_connect_options " "\"{active,false}\" " "-kernel inet_default_listen_options " "\"{active,false}\"", fun do_delay_send_7/0), - ?line {true,true,true} = + {true,true,true} = do_delay_on_other_node("-kernel inet_default_connect_options " "\"[{active,false},{delay_send,true}]\" " "-kernel inet_default_listen_options " "\"[{active,false},{delay_send,true}]\"", fun do_delay_send_7/0), - ?line {true,true,true} = + {true,true,true} = do_delay_on_other_node("-kernel inet_default_connect_options " "\"[{active,false},{delay_send,true}]\" " "-kernel inet_default_listen_options " @@ -203,13 +193,11 @@ default_options(Config) when is_list(Config) -> do_delay_on_other_node(XArgs, Function) -> Dir = filename:dirname(code:which(?MODULE)), - {ok,Node} = test_server:start_node(test_default_options_slave,slave, - [{args,"-pa " ++ Dir ++ " " ++ - XArgs}]), + {ok,Node} = test_server:start_node(?UNIQ_NODE_NAME, slave, + [{args,"-pa " ++ Dir ++ " " ++ XArgs}]), Res = rpc:call(Node,erlang,apply,[Function,[]]), test_server:stop_node(Node), Res. - do_delay_send_1() -> {ok,LS}=gen_tcp:listen(0,[{delay_send,true}]), @@ -301,30 +289,26 @@ do_delay_send_7() -> gen_tcp:close(S), gen_tcp:close(LS), {B1,B2,B3}. - - -controlling_process(doc) -> - ["Open a listen port and change controlling_process for it", - "The result should be ok of done by the owner process," - "Otherwise is should return {error,not_owner} or similar"]; -controlling_process(suite) -> []; +%% Open a listen port and change controlling_process for it +%% The result should be ok of done by the owner process, +%% Otherwise is should return {error,not_owner} or similar. controlling_process(Config) when is_list(Config) -> {ok,S} = gen_tcp:listen(0,[]), Pid2 = spawn(?MODULE,not_owner,[S]), Pid2 ! {self(),2,control}, - ?line {error, E} = receive {2,_E} -> + {error, E} = receive {2,_E} -> _E after 10000 -> timeout end, io:format("received ~p~n",[E]), Pid = spawn(?MODULE,not_owner,[S]), - ?line ok = gen_tcp:controlling_process(S,Pid), + ok = gen_tcp:controlling_process(S,Pid), Pid ! {self(),1,control}, - ?line ok = receive {1,ok} -> - ok - after 1000 -> timeout - end, + ok = receive {1,ok} -> + ok + after 1000 -> timeout + end, Pid ! close. not_owner(S) -> @@ -337,9 +321,8 @@ not_owner(S) -> ok end. -controlling_process_self(doc) -> - ["Open a listen port and assign the controlling process to " - "it self, then exit and make sure the port is closed properly."]; +%% Open a listen port and assign the controlling process to +%% it self, then exit and make sure the port is closed properly. controlling_process_self(Config) when is_list(Config) -> S = self(), process_flag(trap_exit,true), @@ -363,11 +346,9 @@ controlling_process_self(Config) when is_list(Config) -> end. -no_accept(doc) -> - ["Open a listen port and connect to it, then close the listen port ", - "without doing any accept. The connected socket should receive ", - "a tcp_closed message."]; -no_accept(suite) -> []; +%% Open a listen port and connect to it, then close the listen port +%% without doing any accept. The connected socket should receive +%% a tcp_closed message. no_accept(Config) when is_list(Config) -> {ok, L} = gen_tcp:listen(0, []), {ok, {_, Port}} = inet:sockname(L), @@ -377,39 +358,37 @@ no_accept(Config) when is_list(Config) -> {tcp_closed, Client} -> ok after 5000 -> - ?line test_server:fail(never_closed) + ct:fail(never_closed) end. -close_with_pending_output(doc) -> - ["Send several packets to a socket and close it. All packets should arrive ", - "to the other end."]; -close_with_pending_output(suite) -> []; +%% Send several packets to a socket and close it. All packets should +%% arrive to the other end. close_with_pending_output(Config) when is_list(Config) -> - ?line {ok, L} = gen_tcp:listen(0, [binary, {active, false}]), - ?line {ok, {_, Port}} = inet:sockname(L), - ?line Packets = 16, - ?line Total = 2048*Packets, + {ok, L} = gen_tcp:listen(0, [binary, {active, false}]), + {ok, {_, Port}} = inet:sockname(L), + Packets = 16, + Total = 2048*Packets, case start_remote(close_pending) of {ok, Node} -> - ?line {ok, Host} = inet:gethostname(), - ?line spawn_link(Node, ?MODULE, sender, [Port, Packets, Host]), - ?line {ok, A} = gen_tcp:accept(L), - ?line case gen_tcp:recv(A, Total) of + {ok, Host} = inet:gethostname(), + spawn_link(Node, ?MODULE, sender, [Port, Packets, Host]), + {ok, A} = gen_tcp:accept(L), + case gen_tcp:recv(A, Total) of {ok, Bin} when byte_size(Bin) == Total -> gen_tcp:close(A), gen_tcp:close(L); {ok, Bin} -> - ?line test_server:fail({small_packet, + ct:fail({small_packet, byte_size(Bin)}); Error -> - ?line test_server:fail({unexpected, Error}) + ct:fail({unexpected, Error}) end, ok; {error, no_remote_hosts} -> {skipped,"No remote hosts"}; {error, Other} -> - ?line ?t:fail({failed_to_start_slave_node, Other}) + ct:fail({failed_to_start_slave_node, Other}) end. sender(Port, Packets, Host) -> @@ -427,9 +406,7 @@ send_loop(Sock, Data, Left) -> send_loop(Sock, Data, Left-1). %% Test {active,N} option -active_n(doc) -> - ["Verify operation of the {active,N} option."]; -active_n(suite) -> []; +%% Verify operation of the {active,N} option. active_n(Config) when is_list(Config) -> N = 3, LS = ok(gen_tcp:listen(0, [{active,N}])), @@ -539,9 +516,7 @@ active_n(Config) when is_list(Config) -> %% I expect propagation of a close to be quite fast %% so 100 ms seems reasonable. -otp_3924(doc) -> - ["Tests that a socket can be closed fast enough."]; -otp_3924(suite) -> []; +%% Tests that a socket can be closed fast enough. otp_3924(Config) when is_list(Config) -> MaxDelay = (case has_superfluous_schedulers() of true -> 4; @@ -556,63 +531,62 @@ otp_3924(Config) when is_list(Config) -> otp_3924_1(MaxDelay). otp_3924_1(MaxDelay) -> - ?line {ok, Node} = start_node(otp_3924), - ?line DataLen = 100*1024, - ?line Data = otp_3924_data(DataLen), - % Repeat the test a couple of times to prevent the test from passing - % by chance. - repeat(10, - fun (N) -> - ?line ok = otp_3924(MaxDelay, Node, Data, DataLen, N) - end), - ?line test_server:stop_node(Node), + {ok, Node} = start_node(otp_3924), + DataLen = 100*1024, + Data = otp_3924_data(DataLen), + %% Repeat the test a couple of times to prevent the test from passing + %% by chance. + repeat(10, fun(N) -> + ok = otp_3924(MaxDelay, Node, Data, DataLen, N) + end), + test_server:stop_node(Node), ok. otp_3924(MaxDelay, Node, Data, DataLen, N) -> - ?line {ok, L} = gen_tcp:listen(0, [list, {active, false}]), - ?line {ok, {_, Port}} = inet:sockname(L), - ?line {ok, Host} = inet:gethostname(), - ?line Sender = spawn_link(Node, - ?MODULE, - otp_3924_sender, - [self(), Host, Port, Data]), - ?line Data = otp_3924_receive_data(L, Sender, MaxDelay, DataLen, N), - ?line ok = gen_tcp:close(L). + {ok, L} = gen_tcp:listen(0, [list, {active, false}]), + {ok, {_, Port}} = inet:sockname(L), + {ok, Host} = inet:gethostname(), + Sender = spawn_link(Node, + ?MODULE, + otp_3924_sender, + [self(), Host, Port, Data]), + Data = otp_3924_receive_data(L, Sender, MaxDelay, DataLen, N), + ok = gen_tcp:close(L). otp_3924_receive_data(LSock, Sender, MaxDelay, Len, N) -> - ?line OP = process_flag(priority, max), - ?line OTE = process_flag(trap_exit, true), - ?line TimeoutRef = make_ref(), - ?line Data = (catch begin - ?line Sender ! start, - ?line {ok, Sock} = gen_tcp:accept(LSock), - ?line D = otp_3924_receive_data(Sock, - TimeoutRef, - MaxDelay, - Len, - [], - 0), - ?line ok = gen_tcp:close(Sock), - D - end), - ?line unlink(Sender), - ?line process_flag(trap_exit, OTE), - ?line process_flag(priority, OP), + OP = process_flag(priority, max), + OTE = process_flag(trap_exit, true), + TimeoutRef = make_ref(), + Data = (catch begin + Sender ! start, + {ok, Sock} = gen_tcp:accept(LSock), + D = otp_3924_receive_data(Sock, + TimeoutRef, + MaxDelay, + Len, + [], + 0), + ok = gen_tcp:close(Sock), + D + end), + unlink(Sender), + process_flag(trap_exit, OTE), + process_flag(priority, OP), receive {'EXIT', _, TimeoutRef} -> - ?line test_server:fail({close_not_fast_enough,MaxDelay,N}); + ct:fail({close_not_fast_enough,MaxDelay,N}); {'EXIT', Sender, Reason} -> - ?line test_server:fail({sender_exited, Reason}); + ct:fail({sender_exited, Reason}); {'EXIT', _Other, Reason} -> - ?line test_server:fail({linked_process_exited, Reason}) + ct:fail({linked_process_exited, Reason}) after 0 -> case Data of {'EXIT', {A,B}} -> - ?line test_server:fail({A,B,N}); + ct:fail({A,B,N}); {'EXIT', Failure} -> - ?line test_server:fail(Failure); + ct:fail(Failure); _ -> - ?line Data + Data end end. @@ -623,12 +597,12 @@ otp_3924_receive_data(Sock, TimeoutRef, MaxDelay, Len, Acc, AccLen) -> NewAccLen = AccLen + length(Data), if NewAccLen == Len -> - ?line {ok, TRef} = timer:exit_after(MaxDelay, + {ok, TRef} = timer:exit_after(MaxDelay, self(), TimeoutRef), - ?line {error, closed} = gen_tcp:recv(Sock, 0), - ?line timer:cancel(TRef), - ?line lists:flatten([Acc, Data]); + {error, closed} = gen_tcp:recv(Sock, 0), + timer:cancel(TRef), + lists:flatten([Acc, Data]); NewAccLen > Len -> exit({received_too_much, NewAccLen}); true -> @@ -669,8 +643,7 @@ otp_3924_sender(Receiver, Host, Port, Data) -> end. -data_before_close(doc) -> - ["Tests that a huge amount of data can be received before a close."]; +%% Tests that a huge amount of data can be received before a close. data_before_close(Config) when is_list(Config) -> {ok, L} = gen_tcp:listen(0, [binary]), {ok, {_, TcpPort}} = inet:sockname(L), @@ -682,7 +655,7 @@ data_before_close(Config) when is_list(Config) -> io:format("Result: ~p", [Result]); {Wrong, Result} -> io:format("Result: ~p", [Result]), - test_server:fail({wrong_count, Wrong}) + ct:fail({wrong_count, Wrong}) end, ok. @@ -707,21 +680,21 @@ make_zero_packet(N) when N rem 2 == 0 -> make_zero_packet(N) -> P = make_zero_packet(N div 2), [0, P|P]. -get_status(doc) -> - ["OTP-2924", - "test that the socket process does not crash when sys:get_status(Pid)", - "is called."]; -get_status(suite) -> []; + +%% OTP-2924. Test that the socket process does not crash when +%% sys:get_status(Pid) is called. get_status(Config) when is_list(Config) -> - ?line {ok,{socket,Pid,_,_}} = gen_tcp:listen(5678,[]), - ?line {status,Pid,_,_} = sys:get_status(Pid). + {ok,{socket,Pid,_,_}} = gen_tcp:listen(5678,[]), + {status,Pid,_,_} = sys:get_status(Pid). -define(RECOVER_SLEEP, 60000). -define(RETRY_SLEEP, 15000). -iter_max_socks(doc) -> - ["Open as many sockets as possible. Do this several times and check ", - "that we get the same number of sockets every time."]; +iter_max_socks() -> + [{timetrap,{minutes,30}}]. + +%% Open as many sockets as possible. Do this several times and check +%% that we get the same number of sockets every time. iter_max_socks(Config) when is_list(Config) -> N = case os:type() of {win32,_} -> 10; _ -> 20 end, %% Run on a different node in order to limit the effect if this test fails. @@ -744,19 +717,19 @@ do_iter_max_socks(N, failed) -> MS = max_socks(), [MS|do_iter_max_socks(N-1, failed)]; do_iter_max_socks(N, First) when is_integer(First) -> - ?line MS = max_socks(), + MS = max_socks(), if MS == First -> - ?line [MS|do_iter_max_socks(N-1, First)]; + [MS|do_iter_max_socks(N-1, First)]; true -> - ?line io:format("Sleeping for ~p seconds...~n", + io:format("Sleeping for ~p seconds...~n", [?RETRY_SLEEP/1000]), - ?line ?t:sleep(?RETRY_SLEEP), - ?line io:format("Trying again...~n", []), - ?line RetryMS = max_socks(), - ?line if RetryMS == First -> - ?line [RetryMS|do_iter_max_socks(N-1, First)]; + ct:sleep(?RETRY_SLEEP), + io:format("Trying again...~n", []), + RetryMS = max_socks(), + if RetryMS == First -> + [RetryMS|do_iter_max_socks(N-1, First)]; true -> - ?line [RetryMS|do_iter_max_socks(N-1, failed)] + [RetryMS|do_iter_max_socks(N-1, failed)] end end. @@ -768,17 +741,17 @@ all_equal([Rule | T]) -> all_equal(Rule, [Rule | T]) -> all_equal(Rule, T); all_equal(_, [_ | _]) -> - ?line ?t:sleep(?RECOVER_SLEEP), % Wait a while and *hope* that we'll - % recover so other tests won't be - % affected. - ?t:fail(max_socket_mismatch); + ct:sleep(?RECOVER_SLEEP), % Wait a while and *hope* that we'll + %% recover so other tests won't be + %% affected. + ct:fail(max_socket_mismatch); all_equal(_Rule, []) -> ok. max_socks() -> - ?line Socks = open_socks(), - ?line N = length(Socks), - ?line lists:foreach(fun(S) -> ok = gen_tcp:close(S) end, Socks), + Socks = open_socks(), + N = length(Socks), + lists:foreach(fun(S) -> ok = gen_tcp:close(S) end, Socks), io:format("Got ~p sockets", [N]), N. @@ -813,22 +786,21 @@ start_remote(Name) -> Pa = filename:dirname(code:which(?MODULE)), test_server:start_node(Name, slave, [{remote, true}, {args, "-pa " ++ Pa}]). -passive_sockets(doc) -> - ["Tests that when 'the other side' on a passive socket closes, the connecting", - "side still can read until the end of data."]; +%% Tests that when 'the other side' on a passive socket closes, the +%% connecting, side still can read until the end of data. passive_sockets(Config) when is_list(Config) -> - ?line spawn_link(?MODULE, passive_sockets_server, - [[{active,false}],self()]), - ?line receive - {socket,Port} -> ok - end, - ?t:sleep(500), - ?line case gen_tcp:connect("localhost", Port, [{active, false}]) of - {ok, Sock} -> - passive_sockets_read(Sock); - Error -> - ?t:fail({"Could not connect to server", Error}) - end. + spawn_link(?MODULE, passive_sockets_server, + [[{active,false}],self()]), + receive + {socket,Port} -> ok + end, + ct:sleep(500), + case gen_tcp:connect("localhost", Port, [{active, false}]) of + {ok, Sock} -> + passive_sockets_read(Sock); + Error -> + ct:fail({"Could not connect to server", Error}) + end. %% %% Read until we get an {error, closed}. If we get another error, this test case @@ -843,62 +815,61 @@ passive_sockets_read(Sock) -> gen_tcp:close(Sock); Error -> gen_tcp:close(Sock), - ?t:fail({"Did not get {error, closed} before other error", Error}) + ct:fail({"Did not get {error, closed} before other error", Error}) end. passive_sockets_server(Opts, Parent) -> - ?line case gen_tcp:listen(0, Opts) of - {ok, LSock} -> - {ok,{_,Port}} = inet:sockname(LSock), - Parent ! {socket,Port}, - passive_sockets_server_accept(LSock); - Error -> - ?t:fail({"Could not create listen socket", Error}) - end. + case gen_tcp:listen(0, Opts) of + {ok, LSock} -> + {ok,{_,Port}} = inet:sockname(LSock), + Parent ! {socket,Port}, + passive_sockets_server_accept(LSock); + Error -> + ct:fail({"Could not create listen socket", Error}) + end. passive_sockets_server_accept(Sock) -> - ?line case gen_tcp:accept(Sock) of - {ok, Socket} -> - ?t:sleep(500), % Simulate latency - passive_sockets_server_send(Socket, 5), - passive_sockets_server_accept(Sock); - Error -> - ?t:fail({"Could not accept connection", Error}) - end. + case gen_tcp:accept(Sock) of + {ok, Socket} -> + timer:sleep(500), % Simulate latency + passive_sockets_server_send(Socket, 5), + passive_sockets_server_accept(Sock); + Error -> + ct:fail({"Could not accept connection", Error}) + end. passive_sockets_server_send(Socket, 0) -> io:format("Closing other end..~n", []), gen_tcp:close(Socket); passive_sockets_server_send(Socket, X) -> - ?line Data = lists:duplicate(1024*X, $a), - ?line case gen_tcp:send(Socket, Data) of - ok -> - ?t:sleep(50), % Simulate some processing. - passive_sockets_server_send(Socket, X-1); - {error, _Reason} -> - ?t:fail("Failed to send data") - end. + Data = lists:duplicate(1024*X, $a), + case gen_tcp:send(Socket, Data) of + ok -> + ct:sleep(50), % Simulate some processing. + passive_sockets_server_send(Socket, X-1); + {error, _Reason} -> + ct:fail("Failed to send data") + end. -accept_closed_by_other_process(doc) -> - ["Tests the return value from gen_tcp:accept when ", - "the socket is closed from another process. (OTP-3817)"]; +%% Tests the return value from gen_tcp:accept when +%% the socket is closed from another process. (OTP-3817) accept_closed_by_other_process(Config) when is_list(Config) -> - ?line Parent = self(), - ?line {ok, ListenSocket} = gen_tcp:listen(0, []), - ?line Child = + Parent = self(), + {ok, ListenSocket} = gen_tcp:listen(0, []), + Child = spawn_link( fun() -> Parent ! {self(), gen_tcp:accept(ListenSocket)} end), - ?line receive after 1000 -> ok end, - ?line ok = gen_tcp:close(ListenSocket), - ?line receive - {Child, {error, closed}} -> - ok; - {Child, Other} -> - ?t:fail({"Wrong result of gen_tcp:accept", Other}) - end. + receive after 1000 -> ok end, + ok = gen_tcp:close(ListenSocket), + receive + {Child, {error, closed}} -> + ok; + {Child, Other} -> + ct:fail({"Wrong result of gen_tcp:accept", Other}) + end. repeat(N, Fun) -> repeat(N, N, Fun). @@ -910,14 +881,11 @@ repeat(_, _, _) -> ok. -closed_socket(suite) -> - []; -closed_socket(doc) -> - ["Tests the response when using a closed socket as argument"]; +%% Tests the response when using a closed socket as argument. closed_socket(Config) when is_list(Config) -> - ?line {ok, LS1} = gen_tcp:listen(0, []), - ?line erlang:yield(), - ?line ok = gen_tcp:close(LS1), + {ok, LS1} = gen_tcp:listen(0, []), + erlang:yield(), + ok = gen_tcp:close(LS1), %% If the following delay is uncommented, the result error values %% below will change from {error, einval} to {error, closed} since %% inet_db then will have noticed that the socket is closed. @@ -925,19 +893,18 @@ closed_socket(Config) when is_list(Config) -> %% in inet_db processes the 'EXIT' message from the port, %% the socket is unregistered. %% - %% ?line test_server:sleep(test_server:seconds(2)), + %% ct:sleep({seconds,2}) %% - ?line {error, R_send} = gen_tcp:send(LS1, "data"), - ?line {error, R_recv} = gen_tcp:recv(LS1, 17), - ?line {error, R_accept} = gen_tcp:accept(LS1), - ?line {error, R_controlling_process} = + {error, R_send} = gen_tcp:send(LS1, "data"), + {error, R_recv} = gen_tcp:recv(LS1, 17), + {error, R_accept} = gen_tcp:accept(LS1), + {error, R_controlling_process} = gen_tcp:controlling_process(LS1, self()), %% - ?line ok = io:format("R_send = ~p~n", [R_send]), - ?line ok = io:format("R_recv = ~p~n", [R_recv]), - ?line ok = io:format("R_accept = ~p~n", [R_accept]), - ?line ok = io:format("R_controlling_process = ~p~n", - [R_controlling_process]), + ok = io:format("R_send = ~p~n", [R_send]), + ok = io:format("R_recv = ~p~n", [R_recv]), + ok = io:format("R_accept = ~p~n", [R_accept]), + ok = io:format("R_controlling_process = ~p~n", [R_controlling_process]), ok. %%% @@ -945,31 +912,30 @@ closed_socket(Config) when is_list(Config) -> %%% shutdown_active(Config) when is_list(Config) -> - ?line shutdown_common(true). + shutdown_common(true). shutdown_passive(Config) when is_list(Config) -> - ?line shutdown_common(false). + shutdown_common(false). shutdown_common(Active) -> - ?line P = sort_server(Active), + P = sort_server(Active), io:format("Sort server port: ~p\n", [P]), - ?line do_sort(P, []), - ?line do_sort(P, ["glurf"]), - ?line do_sort(P, ["abc","nisse","dum"]), - - ?line do_sort(P, [lists:reverse(integer_to_list(I)) || I <- lists:seq(25, 255)]), - ?line do_sort(P, [lists:reverse(integer_to_list(I)) || I <- lists:seq(77, 999)]), - ?line do_sort(P, [lists:reverse(integer_to_list(I)) || I <- lists:seq(25, 55)]), - ?line do_sort(P, []), - ?line do_sort(P, ["apa"]), - ?line do_sort(P, ["kluns","gorilla"]), - ?line do_sort(P, [lists:reverse(integer_to_list(I)) || I <- lists:seq(25, 1233)]), - ?line do_sort(P, []), - + do_sort(P, []), + do_sort(P, ["glurf"]), + do_sort(P, ["abc","nisse","dum"]), + + do_sort(P, [lists:reverse(integer_to_list(I)) || I <- lists:seq(25, 255)]), + do_sort(P, [lists:reverse(integer_to_list(I)) || I <- lists:seq(77, 999)]), + do_sort(P, [lists:reverse(integer_to_list(I)) || I <- lists:seq(25, 55)]), + do_sort(P, []), + do_sort(P, ["apa"]), + do_sort(P, ["kluns","gorilla"]), + do_sort(P, [lists:reverse(integer_to_list(I)) || I <- lists:seq(25, 1233)]), + do_sort(P, []), receive Any -> - ?t:fail({unexpected_message,Any}) + ct:fail({unexpected_message,Any}) after 0 -> ok end. @@ -985,14 +951,14 @@ do_sort(P, List0) -> sort_server(Active) -> Opts = [{exit_on_close,false},{packet,line},{active,Active}], - ?line {ok,L} = gen_tcp:listen(0, Opts), + {ok,L} = gen_tcp:listen(0, Opts), Go = make_ref(), - ?line Pid = spawn_link(fun() -> - receive Go -> sort_server_1(L, Active) end - end), - ?line ok = gen_tcp:controlling_process(L, Pid), - ?line Pid ! Go, - ?line {ok,Port} = inet:port(L), + Pid = spawn_link(fun() -> + receive Go -> sort_server_1(L, Active) end + end), + ok = gen_tcp:controlling_process(L, Pid), + Pid ! Go, + {ok,Port} = inet:port(L), Port. sort_server_1(L, Active) -> @@ -1042,17 +1008,17 @@ shutdown_pending(Config) when is_list(Config) -> Data = [<<N:32>>,ones(N),42], P = a_server(), io:format("Server port: ~p\n", [P]), - ?line {ok,S} = gen_tcp:connect(localhost, P, []), - ?line gen_tcp:send(S, Data), - ?line gen_tcp:shutdown(S, write), - ?line receive - {tcp,S,Msg} -> - io:format("~p\n", [Msg]), - ?line N = list_to_integer(Msg) - 5; - Other -> - ?t:fail({unexpected,Other}) - end, - ok. + {ok,S} = gen_tcp:connect(localhost, P, []), + gen_tcp:send(S, Data), + gen_tcp:shutdown(S, write), + receive + {tcp,S,Msg} -> + io:format("~p\n", [Msg]), + N = list_to_integer(Msg) - 5; + Other -> + ct:fail({unexpected,Other}) + end, + ok. ones(0) -> []; ones(1) -> [1]; @@ -1065,10 +1031,10 @@ shutdown_pending(Config) when is_list(Config) -> end. a_server() -> - ?line {ok,L} = gen_tcp:listen(0, [{exit_on_close,false},{active,false}]), - ?line Pid = spawn_link(fun() -> a_server(L) end), - ?line ok = gen_tcp:controlling_process(L, Pid), - ?line {ok,Port} = inet:port(L), + {ok,L} = gen_tcp:listen(0, [{exit_on_close,false},{active,false}]), + Pid = spawn_link(fun() -> a_server(L) end), + ok = gen_tcp:controlling_process(L, Pid), + {ok,Port} = inet:port(L), Port. a_server(L) -> @@ -1085,24 +1051,328 @@ shutdown_pending(Config) when is_list(Config) -> gen_tcp:close(S) end. +%% +%% Test 'show_econnreset' option +%% + +show_econnreset_active(Config) when is_list(Config) -> + %% First confirm everything works with option turned off. + {ok, L} = gen_tcp:listen(0, []), + {ok, Port} = inet:port(L), + {ok, Client} = gen_tcp:connect(localhost, Port, [{active, false}]), + {ok, S} = gen_tcp:accept(L), + ok = gen_tcp:close(L), + ok = inet:setopts(Client, [{linger, {true, 0}}]), + ok = gen_tcp:close(Client), + receive + {tcp_closed, S} -> + ok; + Other -> + ct:fail({unexpected1, Other}) + after 1000 -> + ct:fail({timeout, {server, no_tcp_closed}}) + end, + + %% Now test with option switched on. + %% Note: We are also testing that the show_econnreset option is + %% inherited from the listening socket by the accepting socket. + {ok, L1} = gen_tcp:listen(0, [{show_econnreset, true}]), + {ok, Port1} = inet:port(L1), + {ok, Client1} = gen_tcp:connect(localhost, Port1, [{active, false}]), + {ok, S1} = gen_tcp:accept(L1), + ok = gen_tcp:close(L1), + ok = inet:setopts(Client1, [{linger, {true, 0}}]), + ok = gen_tcp:close(Client1), + receive + {tcp_error, S1, econnreset} -> + receive + {tcp_closed, S1} -> + ok; + Other1 -> + ct:fail({unexpected2, Other1}) + after 1 -> + ct:fail({timeout, {server, no_tcp_closed}}) + end; + Other2 -> + ct:fail({unexpected3, Other2}) + after 1000 -> + ct:fail({timeout, {server, no_tcp_error}}) + end. + +show_econnreset_active_once(Config) when is_list(Config) -> + %% Now test using {active, once} + {ok, L} = gen_tcp:listen(0, + [{active, false}, + {show_econnreset, true}]), + {ok, Port} = inet:port(L), + {ok, Client} = gen_tcp:connect(localhost, Port, [{active, false}]), + {ok, S} = gen_tcp:accept(L), + ok = gen_tcp:close(L), + ok = inet:setopts(Client, [{linger, {true, 0}}]), + ok = gen_tcp:close(Client), + ok = ct:sleep(20), + ok = receive Msg -> {unexpected_msg, Msg} after 0 -> ok end, + ok = inet:setopts(S, [{active, once}]), + receive + {tcp_error, S, econnreset} -> + receive + {tcp_closed, S} -> + ok; + Other1 -> + ct:fail({unexpected1, Other1}) + after 1 -> + ct:fail({timeout, {server, no_tcp_closed}}) + end; + Other2 -> + ct:fail({unexpected2, Other2}) + after 1000 -> + ct:fail({timeout, {server, no_tcp_error}}) + end. + +show_econnreset_passive(Config) when is_list(Config) -> + %% First confirm everything works with option turned off. + {ok, L} = gen_tcp:listen(0, [{active, false}]), + {ok, Port} = inet:port(L), + {ok, Client} = gen_tcp:connect(localhost, Port, [{active, false}]), + {ok, S} = gen_tcp:accept(L), + ok = gen_tcp:close(L), + ok = inet:setopts(S, [{linger, {true, 0}}]), + ok = gen_tcp:close(S), + ok = ct:sleep(1), + {error, closed} = gen_tcp:recv(Client, 0), + + %% Now test with option switched on. + {ok, L1} = gen_tcp:listen(0, [{active, false}]), + {ok, Port1} = inet:port(L1), + {ok, Client1} = gen_tcp:connect(localhost, Port1, + [{active, false}, + {show_econnreset, true}]), + {ok, S1} = gen_tcp:accept(L1), + ok = gen_tcp:close(L1), + ok = inet:setopts(S1, [{linger, {true, 0}}]), + ok = gen_tcp:close(S1), + ok = ct:sleep(1), + {error, econnreset} = gen_tcp:recv(Client1, 0). + +econnreset_after_sync_send(Config) when is_list(Config) -> + %% First confirm everything works with option turned off. + {ok, L} = gen_tcp:listen(0, [{active, false}]), + {ok, Port} = inet:port(L), + {ok, Client} = gen_tcp:connect(localhost, Port, [{active, false}]), + {ok, S} = gen_tcp:accept(L), + ok = gen_tcp:close(L), + ok = inet:setopts(S, [{linger, {true, 0}}]), + ok = gen_tcp:close(S), + ok = ct:sleep(20), + {error, closed} = gen_tcp:send(Client, "Whatever"), + + %% Now test with option switched on. + {ok, L1} = gen_tcp:listen(0, [{active, false}]), + {ok, Port1} = inet:port(L1), + {ok, Client1} = gen_tcp:connect(localhost, Port1, + [{active, false}, + {show_econnreset, true}]), + {ok, S1} = gen_tcp:accept(L1), + ok = gen_tcp:close(L1), + ok = inet:setopts(S1, [{linger, {true, 0}}]), + ok = gen_tcp:close(S1), + ok = ct:sleep(20), + {error, econnreset} = gen_tcp:send(Client1, "Whatever"). + +econnreset_after_async_send_active(Config) when is_list(Config) -> + {OS, _} = os:type(), + Payload = lists:duplicate(1024 * 1024, $.), + + %% First confirm everything works with option turned off. + {ok, L} = gen_tcp:listen(0, [{active, false}, {recbuf, 4096}]), + {ok, Port} = inet:port(L), + {ok, Client} = gen_tcp:connect(localhost, Port, [{sndbuf, 4096}]), + {ok, S} = gen_tcp:accept(L), + ok = gen_tcp:close(L), + ok = gen_tcp:send(Client, Payload), + case erlang:port_info(Client, queue_size) of + {queue_size, N} when N > 0 -> ok; + {queue_size, 0} when OS =:= win32 -> ok; + {queue_size, 0} = T -> ct:fail(T) + end, + ok = gen_tcp:send(S, "Whatever"), + ok = ct:sleep(20), + ok = inet:setopts(S, [{linger, {true, 0}}]), + ok = gen_tcp:close(S), + ok = ct:sleep(20), + receive + {tcp, Client, "Whatever"} -> + receive + {tcp_closed, Client} -> + ok; + Other1 -> + ct:fail({unexpected1, Other1}) + end; + Other2 -> + ct:fail({unexpected2, Other2}) + end, + + %% Now test with option switched on. + {ok, L1} = gen_tcp:listen(0, [{active, false}, {recbuf, 4096}]), + {ok, Port1} = inet:port(L1), + {ok, Client1} = gen_tcp:connect(localhost, Port1, + [{sndbuf, 4096}, + {show_econnreset, true}]), + {ok, S1} = gen_tcp:accept(L1), + ok = gen_tcp:close(L1), + ok = gen_tcp:send(Client1, Payload), + case erlang:port_info(Client1, queue_size) of + {queue_size, N1} when N1 > 0 -> ok; + {queue_size, 0} when OS =:= win32 -> ok; + {queue_size, 0} = T1 -> ct:fail(T1) + end, + ok = gen_tcp:send(S1, "Whatever"), + ok = ct:sleep(20), + ok = inet:setopts(S1, [{linger, {true, 0}}]), + ok = gen_tcp:close(S1), + ok = ct:sleep(20), + receive + {tcp, Client1, "Whatever"} -> + receive + {tcp_error, Client1, econnreset} -> + receive + {tcp_closed, Client1} -> + ok; + Other3 -> + ct:fail({unexpected3, Other3}) + end; + Other4 -> + ct:fail({unexpected4, Other4}) + end; + Other5 -> + ct:fail({unexpected5, Other5}) + end. + +econnreset_after_async_send_active_once(Config) when is_list(Config) -> + {OS, _} = os:type(), + {ok, L} = gen_tcp:listen(0, [{active, false}, {recbuf, 4096}]), + {ok, Port} = inet:port(L), + {ok, Client} = gen_tcp:connect(localhost, Port, + [{active, false}, + {sndbuf, 4096}, + {show_econnreset, true}]), + {ok,S} = gen_tcp:accept(L), + ok = gen_tcp:close(L), + Payload = lists:duplicate(1024 * 1024, $.), + ok = gen_tcp:send(Client, Payload), + case erlang:port_info(Client, queue_size) of + {queue_size, N} when N > 0 -> ok; + {queue_size, 0} when OS =:= win32 -> ok; + {queue_size, 0} = T -> ct:fail(T) + end, + ok = gen_tcp:send(S, "Whatever"), + ok = ct:sleep(20), + ok = inet:setopts(S, [{linger, {true, 0}}]), + ok = gen_tcp:close(S), + ok = ct:sleep(20), + ok = receive Msg -> {unexpected_msg, Msg} after 0 -> ok end, + ok = inet:setopts(Client, [{active, once}]), + receive + {tcp_error, Client, econnreset} -> + receive + {tcp_closed, Client} -> + ok; + Other -> + ct:fail({unexpected1, Other}) + end; + Other -> + ct:fail({unexpected2, Other}) + end. + +econnreset_after_async_send_passive(Config) when is_list(Config) -> + {OS, _} = os:type(), + Payload = lists:duplicate(1024 * 1024, $.), + + %% First confirm everything works with option turned off. + {ok, L} = gen_tcp:listen(0, [{active, false}, {recbuf, 4096}]), + {ok, Port} = inet:port(L), + {ok, Client} = gen_tcp:connect(localhost, Port, + [{active, false}, + {sndbuf, 4096}]), + {ok, S} = gen_tcp:accept(L), + ok = gen_tcp:close(L), + ok = inet:setopts(S, [{linger, {true, 0}}]), + ok = gen_tcp:send(S, "Whatever"), + ok = gen_tcp:send(Client, Payload), + case erlang:port_info(Client, queue_size) of + {queue_size, N} when N > 0 -> ok; + {queue_size, 0} when OS =:= win32 -> ok; + {queue_size, 0} = T -> ct:fail(T) + end, + ok = gen_tcp:close(S), + ok = ct:sleep(20), + {error, closed} = gen_tcp:recv(Client, 0), + + %% Now test with option switched on. + {ok, L1} = gen_tcp:listen(0, [{active, false}, {recbuf, 4096}]), + {ok, Port1} = inet:port(L1), + {ok, Client1} = gen_tcp:connect(localhost, Port1, + [{active, false}, + {sndbuf, 4096}, + {show_econnreset, true}]), + {ok, S1} = gen_tcp:accept(L1), + ok = gen_tcp:close(L1), + ok = inet:setopts(S1, [{linger, {true, 0}}]), + ok = gen_tcp:send(S1, "Whatever"), + ok = gen_tcp:send(Client1, Payload), + ok = gen_tcp:close(S1), + ok = ct:sleep(20), + {error, econnreset} = gen_tcp:recv(Client1, 0). + +%% +%% Test {linger {true, 0}} aborts a connection +%% + +linger_zero(Config) when is_list(Config) -> + %% All the econnreset tests will prove that {linger, {true, 0}} aborts + %% a connection when the driver queue is empty. We will test here + %% that it also works when the driver queue is not empty. + {OS, _} = os:type(), + {ok, L} = gen_tcp:listen(0, [{active, false}, + {recbuf, 4096}, + {show_econnreset, true}]), + {ok, Port} = inet:port(L), + {ok, Client} = gen_tcp:connect(localhost, Port, + [{active, false}, + {sndbuf, 4096}]), + {ok, S} = gen_tcp:accept(L), + ok = gen_tcp:close(L), + PayloadSize = 1024 * 1024, + Payload = lists:duplicate(PayloadSize, $.), + ok = gen_tcp:send(Client, Payload), + case erlang:port_info(Client, queue_size) of + {queue_size, N} when N > 0 -> ok; + {queue_size, 0} when OS =:= win32 -> ok; + {queue_size, 0} = T -> ct:fail(T) + end, + ok = inet:setopts(Client, [{linger, {true, 0}}]), + ok = gen_tcp:close(Client), + ok = ct:sleep(1), + undefined = erlang:port_info(Client, connected), + {error, econnreset} = gen_tcp:recv(S, PayloadSize). + %% Thanks to Luke Gorrie. Tests for a very specific problem with %% corrupt data. The testcase will be killed by the timetrap timeout %% if the bug is present. http_bad_packet(Config) when is_list(Config) -> - ?line {ok,L} = gen_tcp:listen(0, - [{active, false}, - binary, - {reuseaddr, true}, - {packet, http}]), - ?line {ok,Port} = inet:port(L), - ?line spawn_link(fun() -> erlang:yield(), http_bad_client(Port) end), - ?line case gen_tcp:accept(L) of - {ok,S} -> - http_worker(S); - Err -> - exit({accept,Err}) - end. + {ok,L} = gen_tcp:listen(0, [{active, false}, + binary, + {reuseaddr, true}, + {packet, http}]), + {ok,Port} = inet:port(L), + spawn_link(fun() -> erlang:yield(), http_bad_client(Port) end), + case gen_tcp:accept(L) of + {ok,S} -> + http_worker(S); + Err -> + exit({accept,Err}) + end. http_worker(S) -> case gen_tcp:recv(S, 0, 30000) of @@ -1122,9 +1392,9 @@ http_bad_client(Port) -> %% Fill send queue and then start receiving. %% busy_send(Config) when is_list(Config) -> - ?line Master = self(), - ?line Msg = <<"the quick brown fox jumps over a lazy dog~n">>, - ?line Server = + Master = self(), + Msg = <<"the quick brown fox jumps over a lazy dog~n">>, + Server = spawn_link(fun () -> {ok,L} = gen_tcp:listen (0, [{active,false},binary, @@ -1134,45 +1404,42 @@ busy_send(Config) when is_list(Config) -> busy_send_client(Port, Master, Msg)}, busy_send_srv(L, Master, Msg) end), - ?line io:format("~p Server~n", [Server]), - ?line receive - {Server,client,Client} -> - ?line io:format("~p Client~n", [Client]), - ?line busy_send_loop(Server, Client, 0) - end. + io:format("~p Server~n", [Server]), + receive + {Server,client,Client} -> + io:format("~p Client~n", [Client]), + busy_send_loop(Server, Client, 0) + end. busy_send_loop(Server, Client, N) -> %% Master %% - ?line receive {Server,send} -> + receive {Server,send} -> busy_send_loop(Server, Client, N+1) after 2000 -> %% Send queue full, sender blocked %% -> stop sender and release client - ?line io:format("Send timeout, time to receive...~n", []), - ?line Server ! {self(),close}, - ?line Client ! {self(),recv,N+1}, - ?line receive - {Server,send} -> - ?line busy_send_2(Server, Client, N+1) - after 10000 -> - %% If this happens, see busy_send_srv - ?t:fail({timeout,{server,not_send,flush([])}}) - end - end. + io:format("Send timeout, time to receive...~n", []), + Server ! {self(),close}, + Client ! {self(),recv,N+1}, + receive + {Server,send} -> + busy_send_2(Server, Client, N+1) + after 10000 -> + %% If this happens, see busy_send_srv + ct:fail({timeout,{server,not_send,flush([])}}) + end + end. busy_send_2(Server, Client, _N) -> %% Master %% - ?line receive - {Server,[closed]} -> - ?line receive - {Client,[0,{error,closed}]} -> - ok - end - after 10000 -> - ?t:fail({timeout,{server,not_closed,flush([])}}) - end. + receive + {Server,[closed]} -> + receive {Client,[0,{error,closed}]} -> ok end + after 10000 -> + ct:fail({timeout,{server,not_closed,flush([])}}) + end. busy_send_srv(L, Master, Msg) -> %% Server @@ -1228,7 +1495,7 @@ busy_send_client_loop(Socket, Master, Msg, N) -> busy_disconnect_passive(Config) when is_list(Config) -> MuchoData = list_to_binary(ones(64*1024)), - ?line [do_busy_disconnect_passive(MuchoData) || _ <- lists:seq(1, 10)], + [do_busy_disconnect_passive(MuchoData) || _ <- lists:seq(1, 10)], ok. do_busy_disconnect_passive(MuchoData) -> @@ -1236,8 +1503,8 @@ do_busy_disconnect_passive(MuchoData) -> busy_disconnect_passive_send(S, MuchoData). busy_disconnect_passive_send(S, Data) -> - ?line case gen_tcp:send(S, Data) of - ok -> ?line busy_disconnect_passive_send(S, Data); + case gen_tcp:send(S, Data) of + ok -> busy_disconnect_passive_send(S, Data); {error,closed} -> ok end. @@ -1248,7 +1515,7 @@ busy_disconnect_passive_send(S, Data) -> %%% busy_disconnect_active(Config) when is_list(Config) -> MuchoData = list_to_binary(ones(64*1024)), - ?line [do_busy_disconnect_active(MuchoData) || _ <- lists:seq(1, 10)], + [do_busy_disconnect_active(MuchoData) || _ <- lists:seq(1, 10)], ok. do_busy_disconnect_active(MuchoData) -> @@ -1256,21 +1523,21 @@ do_busy_disconnect_active(MuchoData) -> busy_disconnect_active_send(S, MuchoData). busy_disconnect_active_send(S, Data) -> - ?line case gen_tcp:send(S, Data) of - ok -> ?line busy_disconnect_active_send(S, Data); + case gen_tcp:send(S, Data) of + ok -> busy_disconnect_active_send(S, Data); {error,closed} -> receive {tcp_closed,S} -> ok; - _Other -> ?line ?t:fail() + _Other -> ct:fail(failed) end end. busy_disconnect_prepare_server(ConnectOpts) -> - ?line Sender = self(), - ?line Server = spawn_link(fun() -> busy_disconnect_server(Sender) end), + Sender = self(), + Server = spawn_link(fun() -> busy_disconnect_server(Sender) end), receive {port,Server,Port} -> ok end, - ?line {ok,S} = gen_tcp:connect(localhost, Port, ConnectOpts), + {ok,S} = gen_tcp:connect(localhost, Port, ConnectOpts), Server ! {Sender,sending}, S. @@ -1304,55 +1571,59 @@ busy_disconnect_server_wait_for_busy(Sender, S) -> %%% Fill send queue %%% fill_sendq(Config) when is_list(Config) -> - ?line Master = self(), - ?line Server = + Master = self(), + Server = spawn_link(fun () -> - {ok,L} = gen_tcp:listen - (0, [{active,false},binary, - {reuseaddr,true},{packet,0}]), + {ok,L} = gen_tcp:listen(0, [{active,false},binary, + {reuseaddr,true},{packet,0}]), {ok,Port} = inet:port(L), Master ! {self(),client, fill_sendq_client(Port, Master)}, fill_sendq_srv(L, Master) end), - ?line io:format("~p Server~n", [Server]), - ?line receive {Server,client,Client} -> - ?line io:format("~p Client~n", [Client]), - ?line receive {Server,reader,Reader} -> - ?line io:format("~p Reader~n", [Reader]), - ?line fill_sendq_loop(Server, Client, Reader) + io:format("~p Server~n", [Server]), + receive + {Server,client,Client} -> + io:format("~p Client~n", [Client]), + receive + {Server,reader,Reader} -> + io:format("~p Reader~n", [Reader]), + fill_sendq_loop(Server, Client, Reader) end end. fill_sendq_loop(Server, Client, Reader) -> %% Master %% - receive {Server,send} -> + receive + {Server,send} -> fill_sendq_loop(Server, Client, Reader) after 2000 -> %% Send queue full, sender blocked -> close client. - ?line io:format("Send timeout, closing Client...~n", []), - ?line Client ! {self(),close}, - ?line receive {Server,[{error,closed}]} -> - ?line io:format("Got server closed.~n"), - ?line receive {Reader,[{error,closed}]} -> - ?line io:format - ("Got reader closed.~n"), - ok - after 3000 -> - ?t:fail({timeout,{closed,reader}}) - end; - {Reader,[{error,closed}]} -> - ?line io:format("Got reader closed.~n"), - ?line receive {Server,[{error,closed}]} -> - ?line io:format("Got server closed~n"), - ok - after 3000 -> - ?t:fail({timeout,{closed,server}}) - end - after 3000 -> - ?t:fail({timeout,{closed,[server,reader]}}) - end + io:format("Send timeout, closing Client...~n", []), + Client ! {self(),close}, + receive + {Server,[{error,closed}]} -> + io:format("Got server closed.~n"), + receive + {Reader,[{error,closed}]} -> + io:format("Got reader closed.~n"), + ok + after 3000 -> + ct:fail({timeout,{closed,reader}}) + end; + {Reader,[{error,closed}]} -> + io:format("Got reader closed.~n"), + receive + {Server,[{error,closed}]} -> + io:format("Got server closed~n"), + ok + after 3000 -> + ct:fail({timeout,{closed,server}}) + end + after 3000 -> + ct:fail({timeout,{closed,[server,reader]}}) + end end. fill_sendq_srv(L, Master) -> @@ -1416,39 +1687,39 @@ fill_sendq_client(Port, Master) -> %%% a closed socket. %%% partial_recv_and_close(Config) when is_list(Config) -> - ?line Msg = "the quick brown fox jumps over a lazy dog 0123456789\n", - ?line Len = length(Msg), - ?line {ok,L} = gen_tcp:listen(0, [{active,false}]), - ?line {ok,P} = inet:port(L), - ?line {ok,S} = gen_tcp:connect("localhost", P, [{active,false}]), - ?line {ok,A} = gen_tcp:accept(L), - ?line ok = gen_tcp:send(S, Msg), - ?line ok = gen_tcp:close(S), - ?line {error,closed} = gen_tcp:recv(A, Len+1), + Msg = "the quick brown fox jumps over a lazy dog 0123456789\n", + Len = length(Msg), + {ok,L} = gen_tcp:listen(0, [{active,false}]), + {ok,P} = inet:port(L), + {ok,S} = gen_tcp:connect("localhost", P, [{active,false}]), + {ok,A} = gen_tcp:accept(L), + ok = gen_tcp:send(S, Msg), + ok = gen_tcp:close(S), + {error,closed} = gen_tcp:recv(A, Len+1), ok. %%% Try to receive more than available number of bytes from %%% a closed socket, this time waiting in the recv before closing. %%% partial_recv_and_close_2(Config) when is_list(Config) -> - ?line Msg = "the quick brown fox jumps over a lazy dog 0123456789\n", - ?line Len = length(Msg), - ?line {ok,L} = gen_tcp:listen(0, [{active,false}]), - ?line {ok,P} = inet:port(L), - ?line Server = self(), - ?line Client = + Msg = "the quick brown fox jumps over a lazy dog 0123456789\n", + Len = length(Msg), + {ok,L} = gen_tcp:listen(0, [{active,false}]), + {ok,P} = inet:port(L), + Server = self(), + Client = spawn_link( fun () -> receive after 2000 -> ok end, {ok,S} = gen_tcp:connect("localhost", P, [{active,false}]), - ?line ok = gen_tcp:send(S, Msg), + ok = gen_tcp:send(S, Msg), receive {Server,close} -> ok end, receive after 2000 -> ok end, - ?line ok = gen_tcp:close(S) + ok = gen_tcp:close(S) end), - ?line {ok,A} = gen_tcp:accept(L), - ?line Client ! {Server,close}, - ?line {error,closed} = gen_tcp:recv(A, Len+1), + {ok,A} = gen_tcp:accept(L), + Client ! {Server,close}, + {error,closed} = gen_tcp:recv(A, Len+1), ok. %%% Here we tests that gen_tcp:recv/2 will return {error,closed} following @@ -1471,161 +1742,154 @@ do_partial_recv_and_close_3() -> receive {port,Port} -> ok end, - ?line Much = ones(8*64*1024), - ?line {ok,S} = gen_tcp:connect(localhost, Port, [{active,false}]), + Much = ones(8*64*1024), + {ok,S} = gen_tcp:connect(localhost, Port, [{active,false}]), %% Send a lot of data (most of it will be queued). The receiver will read one byte %% and close the connection. The write operation will fail. - ?line gen_tcp:send(S, Much), + gen_tcp:send(S, Much), %% We should always get {error,closed} here. - ?line {error,closed} = gen_tcp:recv(S, 0). + {error,closed} = gen_tcp:recv(S, 0). test_prio_put_get() -> Tos = 3 bsl 5, - ?line {ok,L1} = gen_tcp:listen(0, [{active,false}]), - ?line ok = inet:setopts(L1,[{priority,3}]), - ?line ok = inet:setopts(L1,[{tos,Tos}]), - ?line {ok,[{priority,3},{tos,Tos}]} = inet:getopts(L1,[priority,tos]), - ?line ok = inet:setopts(L1,[{priority,3}]), % Dont destroy each other - ?line {ok,[{priority,3},{tos,Tos}]} = inet:getopts(L1,[priority,tos]), - ?line ok = inet:setopts(L1,[{reuseaddr,true}]), % Dont let others destroy - ?line {ok,[{priority,3},{tos,Tos}]} = inet:getopts(L1,[priority,tos]), - ?line gen_tcp:close(L1), + {ok,L1} = gen_tcp:listen(0, [{active,false}]), + ok = inet:setopts(L1,[{priority,3}]), + ok = inet:setopts(L1,[{tos,Tos}]), + {ok,[{priority,3},{tos,Tos}]} = inet:getopts(L1,[priority,tos]), + ok = inet:setopts(L1,[{priority,3}]), % Dont destroy each other + {ok,[{priority,3},{tos,Tos}]} = inet:getopts(L1,[priority,tos]), + ok = inet:setopts(L1,[{reuseaddr,true}]), % Dont let others destroy + {ok,[{priority,3},{tos,Tos}]} = inet:getopts(L1,[priority,tos]), + gen_tcp:close(L1), ok. test_prio_accept() -> - ?line {ok,Sock}=gen_tcp:listen(0,[binary,{packet,0},{active,false}, - {reuseaddr,true},{priority,4}]), - ?line {ok,Port} = inet:port(Sock), - ?line {ok,Sock2}=gen_tcp:connect("localhost",Port,[binary,{packet,0}, - {active,false}, - {reuseaddr,true}, - {priority,4}]), - ?line {ok,Sock3}=gen_tcp:accept(Sock), - ?line {ok,[{priority,4}]} = inet:getopts(Sock,[priority]), - ?line {ok,[{priority,4}]} = inet:getopts(Sock2,[priority]), - ?line {ok,[{priority,4}]} = inet:getopts(Sock3,[priority]), - ?line gen_tcp:close(Sock), - ?line gen_tcp:close(Sock2), - ?line gen_tcp:close(Sock3), + {ok,Sock}=gen_tcp:listen(0,[binary,{packet,0},{active,false}, + {reuseaddr,true},{priority,4}]), + {ok,Port} = inet:port(Sock), + {ok,Sock2}=gen_tcp:connect("localhost",Port,[binary,{packet,0}, + {active,false}, + {reuseaddr,true}, + {priority,4}]), + {ok,Sock3}=gen_tcp:accept(Sock), + {ok,[{priority,4}]} = inet:getopts(Sock,[priority]), + {ok,[{priority,4}]} = inet:getopts(Sock2,[priority]), + {ok,[{priority,4}]} = inet:getopts(Sock3,[priority]), + gen_tcp:close(Sock), + gen_tcp:close(Sock2), + gen_tcp:close(Sock3), ok. test_prio_accept2() -> Tos1 = 4 bsl 5, Tos2 = 3 bsl 5, - ?line {ok,Sock}=gen_tcp:listen(0,[binary,{packet,0},{active,false}, - {reuseaddr,true},{priority,4}, - {tos,Tos1}]), - ?line {ok,Port} = inet:port(Sock), - ?line {ok,Sock2}=gen_tcp:connect("localhost",Port,[binary,{packet,0}, - {active,false}, - {reuseaddr,true}, - {priority,4}, - {tos,Tos2}]), - ?line {ok,Sock3}=gen_tcp:accept(Sock), - ?line {ok,[{priority,4},{tos,Tos1}]} = inet:getopts(Sock,[priority,tos]), - ?line {ok,[{priority,4},{tos,Tos2}]} = inet:getopts(Sock2,[priority,tos]), - ?line {ok,[{priority,4},{tos,Tos1}]} = inet:getopts(Sock3,[priority,tos]), - ?line gen_tcp:close(Sock), - ?line gen_tcp:close(Sock2), - ?line gen_tcp:close(Sock3), + {ok,Sock}=gen_tcp:listen(0,[binary,{packet,0},{active,false}, + {reuseaddr,true},{priority,4}, + {tos,Tos1}]), + {ok,Port} = inet:port(Sock), + {ok,Sock2}=gen_tcp:connect("localhost",Port,[binary,{packet,0}, + {active,false}, + {reuseaddr,true}, + {priority,4}, + {tos,Tos2}]), + {ok,Sock3}=gen_tcp:accept(Sock), + {ok,[{priority,4},{tos,Tos1}]} = inet:getopts(Sock,[priority,tos]), + {ok,[{priority,4},{tos,Tos2}]} = inet:getopts(Sock2,[priority,tos]), + {ok,[{priority,4},{tos,Tos1}]} = inet:getopts(Sock3,[priority,tos]), + gen_tcp:close(Sock), + gen_tcp:close(Sock2), + gen_tcp:close(Sock3), ok. test_prio_accept3() -> Tos1 = 4 bsl 5, Tos2 = 3 bsl 5, - ?line {ok,Sock}=gen_tcp:listen(0,[binary,{packet,0},{active,false}, - {reuseaddr,true}, - {tos,Tos1}]), - ?line {ok,Port} = inet:port(Sock), - ?line {ok,Sock2}=gen_tcp:connect("localhost",Port,[binary,{packet,0}, - {active,false}, - {reuseaddr,true}, - {tos,Tos2}]), - ?line {ok,Sock3}=gen_tcp:accept(Sock), - ?line {ok,[{priority,0},{tos,Tos1}]} = inet:getopts(Sock,[priority,tos]), - ?line {ok,[{priority,0},{tos,Tos2}]} = inet:getopts(Sock2,[priority,tos]), - ?line {ok,[{priority,0},{tos,Tos1}]} = inet:getopts(Sock3,[priority,tos]), - ?line gen_tcp:close(Sock), - ?line gen_tcp:close(Sock2), - ?line gen_tcp:close(Sock3), + {ok,Sock}=gen_tcp:listen(0,[binary,{packet,0},{active,false}, + {reuseaddr,true}, + {tos,Tos1}]), + {ok,Port} = inet:port(Sock), + {ok,Sock2}=gen_tcp:connect("localhost",Port,[binary,{packet,0}, + {active,false}, + {reuseaddr,true}, + {tos,Tos2}]), + {ok,Sock3}=gen_tcp:accept(Sock), + {ok,[{priority,0},{tos,Tos1}]} = inet:getopts(Sock,[priority,tos]), + {ok,[{priority,0},{tos,Tos2}]} = inet:getopts(Sock2,[priority,tos]), + {ok,[{priority,0},{tos,Tos1}]} = inet:getopts(Sock3,[priority,tos]), + gen_tcp:close(Sock), + gen_tcp:close(Sock2), + gen_tcp:close(Sock3), ok. test_prio_accept_async() -> Tos1 = 4 bsl 5, Tos2 = 3 bsl 5, Ref = make_ref(), - ?line spawn(?MODULE,priority_server,[{self(),Ref}]), - ?line Port = receive - {Ref,P} -> P - after 5000 -> ?t:fail({error,"helper process timeout"}) - end, - ?line receive - after 3000 -> ok - end, - ?line {ok,Sock2}=gen_tcp:connect("localhost",Port,[binary,{packet,0}, - {active,false}, - {reuseaddr,true}, - {priority,4}, - {tos,Tos2}]), - ?line receive - {Ref,{ok,[{priority,4},{tos,Tos1}]}} -> - ok ; - {Ref,Error} -> - ?t:fail({missmatch,Error}) - after 5000 -> ?t:fail({error,"helper process timeout"}) - end, - ?line receive - {Ref,{ok,[{priority,4},{tos,Tos1}]}} -> - ok ; - {Ref,Error2} -> - ?t:fail({missmatch,Error2}) - after 5000 -> ?t:fail({error,"helper process timeout"}) - end, - - ?line {ok,[{priority,4},{tos,Tos2}]} = inet:getopts(Sock2,[priority,tos]), - ?line catch gen_tcp:close(Sock2), + spawn(?MODULE,priority_server,[{self(),Ref}]), + Port = receive + {Ref,P} -> P + after 5000 -> ct:fail({error,"helper process timeout"}) + end, + receive + after 3000 -> ok + end, + {ok,Sock2}=gen_tcp:connect("localhost",Port,[binary,{packet,0}, + {active,false}, + {reuseaddr,true}, + {priority,4}, + {tos,Tos2}]), + receive + {Ref,{ok,[{priority,4},{tos,Tos1}]}} -> + ok; + {Ref,Error} -> + ct:fail({missmatch,Error}) + after 5000 -> ct:fail({error,"helper process timeout"}) + end, + receive + {Ref,{ok,[{priority,4},{tos,Tos1}]}} -> + ok; + {Ref,Error2} -> + ct:fail({missmatch,Error2}) + after 5000 -> ct:fail({error,"helper process timeout"}) + end, + + {ok,[{priority,4},{tos,Tos2}]} = inet:getopts(Sock2,[priority,tos]), + catch gen_tcp:close(Sock2), ok. priority_server({Parent,Ref}) -> Tos1 = 4 bsl 5, - ?line {ok,Sock}=gen_tcp:listen(0,[binary,{packet,0},{active,false}, - {reuseaddr,true},{priority,4}, - {tos,Tos1}]), - ?line {ok,Port} = inet:port(Sock), + {ok,Sock}=gen_tcp:listen(0,[binary,{packet,0},{active,false}, + {reuseaddr,true},{priority,4}, + {tos,Tos1}]), + {ok,Port} = inet:port(Sock), Parent ! {Ref,Port}, - ?line {ok,Sock3}=gen_tcp:accept(Sock), + {ok,Sock3}=gen_tcp:accept(Sock), Parent ! {Ref, inet:getopts(Sock,[priority,tos])}, Parent ! {Ref, inet:getopts(Sock3,[priority,tos])}, ok. test_prio_fail() -> - ?line {ok,L} = gen_tcp:listen(0, [{active,false}]), - ?line {error,_} = inet:setopts(L,[{priority,1000}]), -% This error could only happen in linux kernels earlier than 2.6.24.4 -% Privilege check is now disabled and IP_TOS can never fail (only silently -% be masked). -% ?line {error,_} = inet:setopts(L,[{tos,6 bsl 5}]), - ?line gen_tcp:close(L), + {ok,L} = gen_tcp:listen(0, [{active,false}]), + {error,_} = inet:setopts(L,[{priority,1000}]), + gen_tcp:close(L), ok. test_prio_udp() -> Tos = 3 bsl 5, - ?line {ok,S} = gen_udp:open(0,[{active,false},binary,{tos, Tos}, - {priority,3}]), - ?line {ok,[{priority,3},{tos,Tos}]} = inet:getopts(S,[priority,tos]), - ?line gen_udp:close(S), + {ok,S} = gen_udp:open(0,[{active,false},binary,{tos, Tos}, + {priority,3}]), + {ok,[{priority,3},{tos,Tos}]} = inet:getopts(S,[priority,tos]), + gen_udp:close(S), ok. -so_priority(doc) -> - ["Tests the so_priority and ip_tos options on sockets when applicable."]; -so_priority(suite) -> - []; +%% Tests the so_priority and ip_tos options on sockets when applicable. so_priority(Config) when is_list(Config) -> - ?line {ok,L} = gen_tcp:listen(0, [{active,false}]), - ?line ok = inet:setopts(L,[{priority,1}]), - ?line case inet:getopts(L,[priority]) of + {ok,L} = gen_tcp:listen(0, [{active,false}]), + ok = inet:setopts(L,[{priority,1}]), + case inet:getopts(L,[priority]) of {ok,[{priority,1}]} -> gen_tcp:close(L), test_prio_put_get(), @@ -1641,7 +1905,7 @@ so_priority(Config) when is_list(Config) -> {unix,linux} -> case os:version() of {X,Y,_} when (X > 2) or ((X =:= 2) and (Y >= 4)) -> - ?line ?t:fail({error, + ct:fail({error, "so_priority should work on this " "OS, but does not"}); _ -> @@ -1652,24 +1916,250 @@ so_priority(Config) when is_list(Config) -> end end. + + +%% IP_RECVTOS and IP_RECVTCLASS for IP_PKTOPTIONS +%% does not seem to be implemented in Linux until kernel 3.1 +%% +%% It seems pktoptions does not return valid values +%% for IPv4 connect sockets. On the accept socket +%% we get valid values, but on the connect socket we get +%% the default values for TOS and TTL. +%% +%% Therefore the argument CheckConnect that enables +%% checking the returned values for the connect socket. +%% It is only used for recvtclass that is an IPv6 option +%% and there we get valid values from both socket ends. + +recvtos(_Config) -> + test_pktoptions( + inet, [{recvtos,tos,96}], + fun recvtos_ok/2, + false). + +recvtosttl(_Config) -> + test_pktoptions( + inet, [{recvtos,tos,96},{recvttl,ttl,33}], + fun (OSType, OSVer) -> + recvtos_ok(OSType, OSVer) andalso recvttl_ok(OSType, OSVer) + end, + false). + +recvttl(_Config) -> + test_pktoptions( + inet, [{recvttl,ttl,33}], + fun recvttl_ok/2, + false). + +recvtclass(_Config) -> + {ok,IFs} = inet:getifaddrs(), + case + [Name || + {Name,Opts} <- IFs, + lists:member({addr,{0,0,0,0,0,0,0,1}}, Opts)] + of + [_] -> + test_pktoptions( + inet6, [{recvtclass,tclass,224}], + fun recvtclass_ok/2, + true); + [] -> + {skip,{ipv6_not_supported,IFs}} + end. + +%% These version numbers are the highest noted in daily tests +%% where the test fails for a plausible reason, so +%% skip on that platform. +%% +%% On newer versions it might be fixed, but we'll see about that +%% when machines with newer versions gets installed... +%% If the test still fails for a plausible reason these +%% version numbers simply should be increased. +%% Or maybe we should change to only test on known good +%% platforms - change {unix,_} to false? + +%% pktoptions is not supported for IPv4 +recvtos_ok({unix,openbsd}, OSVer) -> not semver_lt(OSVer, {6,4,0}); +recvtos_ok({unix,darwin}, OSVer) -> not semver_lt(OSVer, {17,6,0}); +recvtos_ok({unix,freebsd}, OSVer) -> not semver_lt(OSVer, {11,2,0}); +%% Using the option returns einval, so it is not implemented. +recvtos_ok({unix,sunos}, OSVer) -> not semver_lt(OSVer, {5,12,0}); +%% Does not return any value - not implemented for pktoptions +recvtos_ok({unix,linux}, OSVer) -> not semver_lt(OSVer, {3,1,0}); +%% +recvtos_ok({unix,_}, _) -> true; +recvtos_ok(_, _) -> false. + +%% pktoptions is not supported for IPv4 +recvttl_ok({unix,openbsd}, OSVer) -> not semver_lt(OSVer, {6,4,0}); +recvttl_ok({unix,darwin}, OSVer) -> not semver_lt(OSVer, {17,6,0}); +recvttl_ok({unix,freebsd}, OSVer) -> not semver_lt(OSVer, {11,2,0}); +%% Using the option returns einval, so it is not implemented. +recvttl_ok({unix,sunos}, OSVer) -> not semver_lt(OSVer, {5,12,0}); +%% +recvttl_ok({unix,linux}, _) -> true; +recvttl_ok({unix,_}, _) -> true; +recvttl_ok(_, _) -> false. + +%% pktoptions is not supported for IPv6 +recvtclass_ok({unix,openbsd}, OSVer) -> not semver_lt(OSVer, {6,4,0}); +recvtclass_ok({unix,darwin}, OSVer) -> not semver_lt(OSVer, {17,6,0}); +recvtclass_ok({unix,sunos}, OSVer) -> not semver_lt(OSVer, {5,12,0}); +%% Using the option returns einval, so it is not implemented. +recvtclass_ok({unix,freebsd}, OSVer) -> not semver_lt(OSVer, {11,2,0}); +%% Does not return any value - not implemented for pktoptions +recvtclass_ok({unix,linux}, OSVer) -> not semver_lt(OSVer, {3,1,0}); +%% +recvtclass_ok({unix,_}, _) -> true; +recvtclass_ok(_, _) -> false. + +semver_lt({X1,Y1,Z1}, {X2,Y2,Z2}) -> + if + X1 > X2 -> false; + X1 < X2 -> true; + Y1 > Y2 -> false; + Y1 < Y2 -> true; + Z1 > Z2 -> false; + Z1 < Z2 -> true; + true -> false + end; +semver_lt(_, {_,_,_}) -> false. + +test_pktoptions(Family, Spec, OSFilter, CheckConnect) -> + OSType = os:type(), + OSVer = os:version(), + case OSFilter(OSType, OSVer) of + true -> + io:format("Os: ~p, ~p~n", [OSType,OSVer]), + test_pktoptions(Family, Spec, CheckConnect, OSType, OSVer); + false -> + {skip,{not_supported_for_os_version,{OSType,OSVer}}} + end. +%% +test_pktoptions(Family, Spec, CheckConnect, OSType, OSVer) -> + Timeout = 5000, + RecvOpts = [RecvOpt || {RecvOpt,_,_} <- Spec], + TrueRecvOpts = [{RecvOpt,true} || {RecvOpt,_,_} <- Spec], + FalseRecvOpts = [{RecvOpt,false} || {RecvOpt,_,_} <- Spec], + Opts = [Opt || {_,Opt,_} <- Spec], + OptsVals = [{Opt,Val} || {_,Opt,Val} <- Spec], + Address = + case Family of + inet -> + {127,0,0,1}; + inet6 -> + {0,0,0,0,0,0,0,1} + end, + %% + %% Set RecvOpts on listen socket + {ok,L} = + gen_tcp:listen( + 0, + [Family,binary,{active,false},{send_timeout,Timeout} + |TrueRecvOpts]), + {ok,P} = inet:port(L), + {ok,TrueRecvOpts} = inet:getopts(L, RecvOpts), + {ok,OptsValsDefault} = inet:getopts(L, Opts), + %% + %% Set RecvOpts and Option values on connect socket + {ok,S2} = + gen_tcp:connect( + Address, P, + [Family,binary,{active,false},{send_timeout,Timeout} + |TrueRecvOpts ++ OptsVals], + Timeout), + {ok,TrueRecvOpts} = inet:getopts(S2, RecvOpts), + {ok,OptsVals} = inet:getopts(S2, Opts), + %% + %% Accept socket inherits the options from listen socket + {ok,S1} = gen_tcp:accept(L, Timeout), + {ok,TrueRecvOpts} = inet:getopts(S1, RecvOpts), + {ok,OptsValsDefault} = inet:getopts(S1, Opts), +%%% %% +%%% %% Handshake +%%% ok = gen_tcp:send(S1, <<"hello">>), +%%% {ok,<<"hello">>} = gen_tcp:recv(S2, 5, Timeout), +%%% ok = gen_tcp:send(S2, <<"hi">>), +%%% {ok,<<"hi">>} = gen_tcp:recv(S1, 2, Timeout), + %% + %% Verify returned remote options + {ok,[{pktoptions,OptsVals1}]} = inet:getopts(S1, [pktoptions]), + {ok,[{pktoptions,OptsVals2}]} = inet:getopts(S2, [pktoptions]), + (Result1 = sets_eq(OptsVals1, OptsVals)) + orelse io:format( + "Accept differs: ~p neq ~p~n", [OptsVals1,OptsVals]), + (Result2 = sets_eq(OptsVals2, OptsValsDefault)) + orelse io:format( + "Connect differs: ~p neq ~p~n", + [OptsVals2,OptsValsDefault]), + %% + ok = gen_tcp:close(S2), + ok = gen_tcp:close(S1), + %% + %% + %% Clear RecvOpts on listen socket and set Option values + ok = inet:setopts(L, FalseRecvOpts ++ OptsVals), + {ok,FalseRecvOpts} = inet:getopts(L, RecvOpts), + {ok,OptsVals} = inet:getopts(L, Opts), + %% + %% Set RecvOpts on connecting socket + %% + {ok,S4} = + gen_tcp:connect( + Address, P, + [Family,binary,{active,false},{send_timeout,Timeout} + |TrueRecvOpts], + Timeout), + {ok,TrueRecvOpts} = inet:getopts(S4, RecvOpts), + {ok,OptsValsDefault} = inet:getopts(S4, Opts), + %% + %% Accept socket inherits the options from listen socket + {ok,S3} = gen_tcp:accept(L, Timeout), + {ok,FalseRecvOpts} = inet:getopts(S3, RecvOpts), + {ok,OptsVals} = inet:getopts(S3, Opts), + %% + %% Verify returned remote options + {ok,[{pktoptions,[]}]} = inet:getopts(S3, [pktoptions]), + {ok,[{pktoptions,OptsVals4}]} = inet:getopts(S4, [pktoptions]), + (Result3 = sets_eq(OptsVals4, OptsVals)) + orelse io:format( + "Accept2 differs: ~p neq ~p~n", [OptsVals4,OptsVals]), + %% + ok = gen_tcp:close(S4), + ok = gen_tcp:close(S3), + ok = gen_tcp:close(L), + (Result1 and ((not CheckConnect) or (Result2 and Result3))) + orelse + exit({failed, + [{OptsVals1,OptsVals4,OptsVals}, + {OptsVals2,OptsValsDefault}], + {OSType,OSVer}}), +%% exit({{OSType,OSVer},success}), % In search for the truth + ok. + +sets_eq(L1, L2) -> + lists:sort(L1) == lists:sort(L2). + + + %% Accept test utilities (suites are below) millis() -> - {A,B,C}=erlang:now(), - (A*1000000*1000)+(B*1000)+(C div 1000). + erlang:monotonic_time(millisecond). -collect_accepts(Tmo) -> +collect_accepts(0,_) -> []; +collect_accepts(N,Tmo) -> A = millis(), receive {accepted,P,Msg} -> - [{P,Msg}] ++ collect_accepts(Tmo-(millis() - A)) + [{P,Msg}] ++ collect_accepts(N-1,Tmo-(millis() - A)) after Tmo -> [] end. --define(EXPECT_ACCEPTS(Pattern,Timeout), +-define(EXPECT_ACCEPTS(Pattern,N,Timeout), (fun() -> - case collect_accepts(Timeout) of + case collect_accepts(if N =:= infinity -> -1; true -> N end,Timeout) of Pattern -> ok; Other -> @@ -1700,240 +2190,239 @@ mktmofun(Tmo,Parent,LS) -> fun() -> Parent ! {accepted,self(), catch gen_tcp:accept(LS,Tmo)} end. %% Accept tests -primitive_accept(suite) -> - []; -primitive_accept(doc) -> - ["Test singular accept"]; +%% Test singular accept. primitive_accept(Config) when is_list(Config) -> - ?line {ok,LS}=gen_tcp:listen(0,[]), - ?line {ok,PortNo}=inet:port(LS), - ?line Parent = self(), - ?line F = fun() -> Parent ! {accepted,self(),gen_tcp:accept(LS)} end, - ?line P = spawn(F), - ?line gen_tcp:connect("localhost",PortNo,[]), - ?line receive - {accepted,P,{ok,P0}} when is_port(P0) -> - ok; - {accepted,P,Other0} -> - {error,Other0} - after 500 -> - {error,timeout} - end. + {ok,LS}=gen_tcp:listen(0,[]), + {ok,PortNo}=inet:port(LS), + Parent = self(), + F = fun() -> Parent ! {accepted,self(),gen_tcp:accept(LS)} end, + P = spawn(F), + gen_tcp:connect("localhost",PortNo,[]), + receive + {accepted,P,{ok,P0}} when is_port(P0) -> + ok; + {accepted,P,Other0} -> + {error,Other0} + after 500 -> + {error,timeout} + end. -multi_accept_close_listen(suite) -> - []; -multi_accept_close_listen(doc) -> - ["Closing listen socket when multi-accepting"]; +%% Closing listen socket when multi-accepting. multi_accept_close_listen(Config) when is_list(Config) -> - ?line {ok,LS}=gen_tcp:listen(0,[]), - ?line Parent = self(), - ?line F = fun() -> Parent ! {accepted,self(),gen_tcp:accept(LS)} end, - ?line spawn(F), - ?line spawn(F), - ?line spawn(F), - ?line spawn(F), - ?line gen_tcp:close(LS), - ?line ?EXPECT_ACCEPTS([{_,{error,closed}},{_,{error,closed}}, - {_,{error,closed}},{_,{error,closed}}], 500). + {ok,LS}=gen_tcp:listen(0,[]), + Parent = self(), + F = fun() -> Parent ! {accepted,self(),gen_tcp:accept(LS)} end, + spawn(F), + spawn(F), + spawn(F), + spawn(F), + gen_tcp:close(LS), + ok = ?EXPECT_ACCEPTS([{_,{error,closed}},{_,{error,closed}}, + {_,{error,closed}},{_,{error,closed}}],4,500). -accept_timeout(suite) -> - []; -accept_timeout(doc) -> - ["Single accept with timeout"]; +%% Single accept with timeout. accept_timeout(Config) when is_list(Config) -> - ?line {ok,LS}=gen_tcp:listen(0,[]), - ?line Parent = self(), - ?line F = fun() -> Parent ! {accepted,self(),gen_tcp:accept(LS,1000)} end, - ?line P = spawn(F), - ?line ?EXPECT_ACCEPTS([{P,{error,timeout}}],2000). + {ok,LS}=gen_tcp:listen(0,[]), + Parent = self(), + F = fun() -> Parent ! {accepted,self(),gen_tcp:accept(LS,1000)} end, + P = spawn(F), + ok = ?EXPECT_ACCEPTS([{P,{error,timeout}}],1,2000). -accept_timeouts_in_order(suite) -> - []; -accept_timeouts_in_order(doc) -> - ["Check that multi-accept timeouts happen in the correct order"]; +%% Check that multi-accept timeouts happen in the correct order. accept_timeouts_in_order(Config) when is_list(Config) -> - ?line {ok,LS}=gen_tcp:listen(0,[]), - ?line Parent = self(), - ?line P1 = spawn(mktmofun(1000,Parent,LS)), - ?line P2 = spawn(mktmofun(1200,Parent,LS)), - ?line P3 = spawn(mktmofun(1300,Parent,LS)), - ?line P4 = spawn(mktmofun(1400,Parent,LS)), - ?line ?EXPECT_ACCEPTS([{P1,{error,timeout}},{P2,{error,timeout}}, - {P3,{error,timeout}},{P4,{error,timeout}}], 2000). - -accept_timeouts_in_order2(suite) -> - []; -accept_timeouts_in_order2(doc) -> - ["Check that multi-accept timeouts happen in the correct order (more)"]; + {ok,LS}=gen_tcp:listen(0,[]), + Parent = self(), + P1 = spawn(mktmofun(1000,Parent,LS)), + P2 = spawn(mktmofun(1200,Parent,LS)), + P3 = spawn(mktmofun(1300,Parent,LS)), + P4 = spawn(mktmofun(1400,Parent,LS)), + ok = ?EXPECT_ACCEPTS([{P1,{error,timeout}},{P2,{error,timeout}}, + {P3,{error,timeout}},{P4,{error,timeout}}],infinity,2000). + +%% Check that multi-accept timeouts happen in the correct order (more). accept_timeouts_in_order2(Config) when is_list(Config) -> - ?line {ok,LS}=gen_tcp:listen(0,[]), - ?line Parent = self(), - ?line P1 = spawn(mktmofun(1400,Parent,LS)), - ?line P2 = spawn(mktmofun(1300,Parent,LS)), - ?line P3 = spawn(mktmofun(1200,Parent,LS)), - ?line P4 = spawn(mktmofun(1000,Parent,LS)), - ?line ?EXPECT_ACCEPTS([{P4,{error,timeout}},{P3,{error,timeout}}, - {P2,{error,timeout}},{P1,{error,timeout}}], 2000). - -accept_timeouts_in_order3(suite) -> - []; -accept_timeouts_in_order3(doc) -> - ["Check that multi-accept timeouts happen in the correct order (even more)"]; + {ok,LS}=gen_tcp:listen(0,[]), + Parent = self(), + P1 = spawn(mktmofun(1400,Parent,LS)), + P2 = spawn(mktmofun(1300,Parent,LS)), + P3 = spawn(mktmofun(1200,Parent,LS)), + P4 = spawn(mktmofun(1000,Parent,LS)), + ok = ?EXPECT_ACCEPTS([{P4,{error,timeout}},{P3,{error,timeout}}, + {P2,{error,timeout}},{P1,{error,timeout}}],infinity,2000). + +%% Check that multi-accept timeouts happen in the correct order (even more). accept_timeouts_in_order3(Config) when is_list(Config) -> - ?line {ok,LS}=gen_tcp:listen(0,[]), - ?line Parent = self(), - ?line P1 = spawn(mktmofun(1200,Parent,LS)), - ?line P2 = spawn(mktmofun(1400,Parent,LS)), - ?line P3 = spawn(mktmofun(1300,Parent,LS)), - ?line P4 = spawn(mktmofun(1000,Parent,LS)), - ?line ?EXPECT_ACCEPTS([{P4,{error,timeout}},{P1,{error,timeout}}, - {P3,{error,timeout}},{P2,{error,timeout}}], 2000). - -accept_timeouts_mixed(suite) -> - []; -accept_timeouts_mixed(doc) -> - ["Check that multi-accept timeouts behave correctly when mixed with successful timeouts"]; + {ok,LS}=gen_tcp:listen(0,[]), + Parent = self(), + P1 = spawn(mktmofun(1200,Parent,LS)), + P2 = spawn(mktmofun(1400,Parent,LS)), + P3 = spawn(mktmofun(1300,Parent,LS)), + P4 = spawn(mktmofun(1000,Parent,LS)), + ok = ?EXPECT_ACCEPTS([{P4,{error,timeout}},{P1,{error,timeout}}, + {P3,{error,timeout}},{P2,{error,timeout}}],infinity,2000). + +%% Check that multi-accept timeouts happen in the correct order after +%% mixing millsec and sec timeouts. +accept_timeouts_in_order4(Config) when is_list(Config) -> + {ok,LS}=gen_tcp:listen(0,[]), + Parent = self(), + P1 = spawn(mktmofun(200,Parent,LS)), + P2 = spawn(mktmofun(400,Parent,LS)), + P3 = spawn(mktmofun(1000,Parent,LS)), + P4 = spawn(mktmofun(600,Parent,LS)), + ok = ?EXPECT_ACCEPTS([{P1,{error,timeout}},{P2,{error,timeout}}, + {P4,{error,timeout}},{P3,{error,timeout}}],infinity,2000). + +%% Check that multi-accept timeouts happen in the correct order after +%% mixing millsec and sec timeouts (more). +accept_timeouts_in_order5(Config) when is_list(Config) -> + {ok,LS}=gen_tcp:listen(0,[]), + Parent = self(), + P1 = spawn(mktmofun(400,Parent,LS)), + P2 = spawn(mktmofun(1000,Parent,LS)), + P3 = spawn(mktmofun(600,Parent,LS)), + P4 = spawn(mktmofun(200,Parent,LS)), + ok = ?EXPECT_ACCEPTS([{P4,{error,timeout}},{P1,{error,timeout}}, + {P3,{error,timeout}},{P2,{error,timeout}}],infinity,2000). + +%% Check that multi-accept timeouts happen in the correct order after +%% mixing millsec and sec timeouts (even more). +accept_timeouts_in_order6(Config) when is_list(Config) -> + {ok,LS}=gen_tcp:listen(0,[]), + Parent = self(), + P1 = spawn(mktmofun(1000,Parent,LS)), + P2 = spawn(mktmofun(400,Parent,LS)), + P3 = spawn(mktmofun(600,Parent,LS)), + P4 = spawn(mktmofun(200,Parent,LS)), + ok = ?EXPECT_ACCEPTS([{P4,{error,timeout}},{P2,{error,timeout}}, + {P3,{error,timeout}},{P1,{error,timeout}}],infinity,2000). + +%% Check that multi-accept timeouts happen in the correct order after +%% mixing millsec and sec timeouts (even more++). +accept_timeouts_in_order7(Config) when is_list(Config) -> + {ok,LS}=gen_tcp:listen(0,[]), + Parent = self(), + P1 = spawn(mktmofun(1000,Parent,LS)), + P2 = spawn(mktmofun(200,Parent,LS)), + P3 = spawn(mktmofun(1200,Parent,LS)), + P4 = spawn(mktmofun(600,Parent,LS)), + P5 = spawn(mktmofun(400,Parent,LS)), + P6 = spawn(mktmofun(800,Parent,LS)), + P7 = spawn(mktmofun(1600,Parent,LS)), + P8 = spawn(mktmofun(1400,Parent,LS)), + ok = ?EXPECT_ACCEPTS([{P2,{error,timeout}},{P5,{error,timeout}}, + {P4,{error,timeout}},{P6,{error,timeout}}, + {P1,{error,timeout}},{P3,{error,timeout}}, + {P8,{error,timeout}},{P7,{error,timeout}}],infinity,2000). + +%% Check that multi-accept timeouts behave correctly when mixed with successful timeouts. accept_timeouts_mixed(Config) when is_list(Config) -> - ?line {ok,LS}=gen_tcp:listen(0,[]), - ?line Parent = self(), - ?line {ok,PortNo}=inet:port(LS), - ?line P1 = spawn(mktmofun(1000,Parent,LS)), - ?line wait_until_accepting(P1,500), - ?line P2 = spawn(mktmofun(2000,Parent,LS)), - ?line wait_until_accepting(P2,500), - ?line P3 = spawn(mktmofun(3000,Parent,LS)), - ?line wait_until_accepting(P3,500), - ?line P4 = spawn(mktmofun(4000,Parent,LS)), - ?line wait_until_accepting(P4,500), - ?line ok = ?EXPECT_ACCEPTS([{P1,{error,timeout}}],1500), - ?line {ok,_}=gen_tcp:connect("localhost",PortNo,[]), - ?line ok = ?EXPECT_ACCEPTS([{P2,{ok,Port0}}] when is_port(Port0),100), - ?line ok = ?EXPECT_ACCEPTS([{P3,{error,timeout}}],2000), - ?line gen_tcp:connect("localhost",PortNo,[]), - ?line ?EXPECT_ACCEPTS([{P4,{ok,Port1}}] when is_port(Port1),100). - -killing_acceptor(suite) -> - []; -killing_acceptor(doc) -> - ["Check that single acceptor behaves as expected when killed"]; + {ok,LS}=gen_tcp:listen(0,[]), + Parent = self(), + {ok,PortNo}=inet:port(LS), + P1 = spawn(mktmofun(1000,Parent,LS)), + wait_until_accepting(P1,500), + P2 = spawn(mktmofun(2000,Parent,LS)), + wait_until_accepting(P2,500), + P3 = spawn(mktmofun(3000,Parent,LS)), + wait_until_accepting(P3,500), + P4 = spawn(mktmofun(4000,Parent,LS)), + wait_until_accepting(P4,500), + ok = ?EXPECT_ACCEPTS([{P1,{error,timeout}}],infinity,1500), + {ok,_}=gen_tcp:connect("localhost",PortNo,[]), + ok = ?EXPECT_ACCEPTS([{P2,{ok,Port0}}] when is_port(Port0),infinity,100), + ok = ?EXPECT_ACCEPTS([{P3,{error,timeout}}],infinity,2000), + gen_tcp:connect("localhost",PortNo,[]), + ok = ?EXPECT_ACCEPTS([{P4,{ok,Port1}}] when is_port(Port1),infinity,100). + +%% Check that single acceptor behaves as expected when killed. killing_acceptor(Config) when is_list(Config) -> - ?line {ok,LS}=gen_tcp:listen(0,[]), - ?line Pid = spawn(fun() -> erlang:display({accepted,self(),gen_tcp:accept(LS)}) end), - ?line receive after 100 -> - ok - end, - ?line {ok,L1} = prim_inet:getstatus(LS), - ?line true = lists:member(accepting, L1), - ?line exit(Pid,kill), - ?line receive after 100 -> - ok - end, - ?line {ok,L2} = prim_inet:getstatus(LS), - ?line false = lists:member(accepting, L2), + {ok,LS}=gen_tcp:listen(0,[]), + Pid = spawn(fun() -> erlang:display({accepted,self(),gen_tcp:accept(LS)}) end), + receive after 100 -> ok + end, + {ok,L1} = prim_inet:getstatus(LS), + true = lists:member(accepting, L1), + exit(Pid,kill), + receive after 100 -> ok + end, + {ok,L2} = prim_inet:getstatus(LS), + false = lists:member(accepting, L2), ok. -killing_multi_acceptors(suite) -> - []; -killing_multi_acceptors(doc) -> - ["Check that multi acceptors behaves as expected when killed"]; +%% Check that multi acceptors behaves as expected when killed. killing_multi_acceptors(Config) when is_list(Config) -> - ?line {ok,LS}=gen_tcp:listen(0,[]), - ?line Parent = self(), - ?line F = fun() -> Parent ! {accepted,self(),gen_tcp:accept(LS)} end, - ?line F2 = mktmofun(1000,Parent,LS), - ?line Pid = spawn(F), - ?line Pid2 = spawn(F2), - ?line receive after 100 -> - ok - end, - ?line {ok,L1} = prim_inet:getstatus(LS), - ?line true = lists:member(accepting, L1), - ?line exit(Pid,kill), - ?line receive after 100 -> - ok - end, - ?line {ok,L2} = prim_inet:getstatus(LS), - ?line true = lists:member(accepting, L2), - ?line ok = ?EXPECT_ACCEPTS([{Pid2,{error,timeout}}],1000), - ?line {ok,L3} = prim_inet:getstatus(LS), - ?line false = lists:member(accepting, L3), + {ok,LS}=gen_tcp:listen(0,[]), + Parent = self(), + F = fun() -> Parent ! {accepted,self(),gen_tcp:accept(LS)} end, + F2 = mktmofun(1000,Parent,LS), + Pid = spawn(F), + Pid2 = spawn(F2), + receive after 100 -> ok + end, + {ok,L1} = prim_inet:getstatus(LS), + true = lists:member(accepting, L1), + exit(Pid,kill), + receive after 100 -> ok + end, + {ok,L2} = prim_inet:getstatus(LS), + true = lists:member(accepting, L2), + ok = ?EXPECT_ACCEPTS([{Pid2,{error,timeout}}],1,1000), + {ok,L3} = prim_inet:getstatus(LS), + false = lists:member(accepting, L3), ok. -killing_multi_acceptors2(suite) -> - []; -killing_multi_acceptors2(doc) -> - ["Check that multi acceptors behaves as expected when killed (more)"]; +%% Check that multi acceptors behaves as expected when killed (more). killing_multi_acceptors2(Config) when is_list(Config) -> - ?line {ok,LS}=gen_tcp:listen(0,[]), - ?line Parent = self(), - ?line {ok,PortNo}=inet:port(LS), - ?line F = fun() -> Parent ! {accepted,self(),gen_tcp:accept(LS)} end, - ?line F2 = mktmofun(1000,Parent,LS), - ?line Pid = spawn(F), - ?line Pid2 = spawn(F), - ?line receive after 100 -> - ok - end, - ?line {ok,L1} = prim_inet:getstatus(LS), - ?line true = lists:member(accepting, L1), - ?line exit(Pid,kill), - ?line receive after 100 -> - ok - end, - ?line {ok,L2} = prim_inet:getstatus(LS), - ?line true = lists:member(accepting, L2), - ?line exit(Pid2,kill), - ?line receive after 100 -> - ok - end, - ?line {ok,L3} = prim_inet:getstatus(LS), - ?line false = lists:member(accepting, L3), - ?line Pid3 = spawn(F2), - ?line receive after 100 -> - ok - end, - ?line {ok,L4} = prim_inet:getstatus(LS), - ?line true = lists:member(accepting, L4), - ?line gen_tcp:connect("localhost",PortNo,[]), - ?line ok = ?EXPECT_ACCEPTS([{Pid3,{ok,Port}}] when is_port(Port),100), - ?line {ok,L5} = prim_inet:getstatus(LS), - ?line false = lists:member(accepting, L5), + {ok,LS}=gen_tcp:listen(0,[]), + Parent = self(), + {ok,PortNo}=inet:port(LS), + F = fun() -> Parent ! {accepted,self(),gen_tcp:accept(LS)} end, + F2 = mktmofun(1000,Parent,LS), + Pid = spawn(F), + Pid2 = spawn(F), + receive after 100 -> ok + end, + {ok,L1} = prim_inet:getstatus(LS), + true = lists:member(accepting, L1), + exit(Pid,kill), + receive after 100 -> ok + end, + {ok,L2} = prim_inet:getstatus(LS), + true = lists:member(accepting, L2), + exit(Pid2,kill), + receive after 100 -> ok + end, + {ok,L3} = prim_inet:getstatus(LS), + false = lists:member(accepting, L3), + Pid3 = spawn(F2), + receive after 100 -> ok + end, + {ok,L4} = prim_inet:getstatus(LS), + true = lists:member(accepting, L4), + gen_tcp:connect("localhost",PortNo,[]), + ok = ?EXPECT_ACCEPTS([{Pid3,{ok,Port}}] when is_port(Port),1,100), + {ok,L5} = prim_inet:getstatus(LS), + false = lists:member(accepting, L5), ok. -several_accepts_in_one_go(suite) -> - []; -several_accepts_in_one_go(doc) -> - ["checks that multi-accept works when more than one accept can be " - "done at once (wb test of inet_driver)"]; +%% Checks that multi-accept works when more than one accept can be +%% done at once (wb test of inet_driver). several_accepts_in_one_go(Config) when is_list(Config) -> - ?line {ok,LS}=gen_tcp:listen(0,[]), - ?line Parent = self(), - ?line {ok,PortNo}=inet:port(LS), - ?line F1 = fun() -> Parent ! {accepted,self(),gen_tcp:accept(LS)} end, - ?line F2 = fun() -> Parent ! {connected,self(),gen_tcp:connect("localhost",PortNo,[])} end, - ?line spawn(F1), - ?line spawn(F1), - ?line spawn(F1), - ?line spawn(F1), - ?line spawn(F1), - ?line spawn(F1), - ?line spawn(F1), - ?line spawn(F1), - ?line ok = ?EXPECT_ACCEPTS([],500), - ?line spawn(F2), - ?line spawn(F2), - ?line spawn(F2), - ?line spawn(F2), - ?line spawn(F2), - ?line spawn(F2), - ?line spawn(F2), - ?line spawn(F2), - ?line ok = ?EXPECT_ACCEPTS([{_,{ok,_}},{_,{ok,_}},{_,{ok,_}},{_,{ok,_}},{_,{ok,_}},{_,{ok,_}},{_,{ok,_}},{_,{ok,_}}],15000), - ?line ok = ?EXPECT_CONNECTS([{_,{ok,_}},{_,{ok,_}},{_,{ok,_}},{_,{ok,_}},{_,{ok,_}},{_,{ok,_}},{_,{ok,_}},{_,{ok,_}}],1000), + {ok,LS}=gen_tcp:listen(0,[]), + Parent = self(), + {ok,PortNo}=inet:port(LS), + F1 = fun() -> Parent ! {accepted,self(),gen_tcp:accept(LS)} end, + F2 = fun() -> Parent ! {connected,self(),gen_tcp:connect("localhost",PortNo,[])} end, + Ns = lists:seq(1,8), + _ = [spawn(F1) || _ <- Ns], + ok = ?EXPECT_ACCEPTS([],1,500), % wait for tmo + _ = [spawn(F2) || _ <- Ns], + ok = ?EXPECT_ACCEPTS([{_,{ok,_}},{_,{ok,_}},{_,{ok,_}},{_,{ok,_}},{_,{ok,_}},{_,{ok,_}},{_,{ok,_}},{_,{ok,_}}],8,15000), + ok = ?EXPECT_CONNECTS([{_,{ok,_}},{_,{ok,_}},{_,{ok,_}},{_,{ok,_}},{_,{ok,_}},{_,{ok,_}},{_,{ok,_}},{_,{ok,_}}],1000), ok. - flush(Msgs) -> erlang:yield(), receive Msg -> flush([Msg|Msgs]) @@ -1944,7 +2433,7 @@ wait_until_accepting(Proc,0) -> exit({timeout_waiting_for_accepting,Proc}); wait_until_accepting(Proc,N) -> case process_info(Proc,current_function) of - {current_function,{prim_inet,accept0,2}} -> + {current_function,{prim_inet,accept0,3}} -> case process_info(Proc,status) of {status,waiting} -> ok; @@ -1962,19 +2451,16 @@ wait_until_accepting(Proc,N) -> end. -accept_system_limit(suite) -> - []; -accept_system_limit(doc) -> - ["Check that accept returns {error, system_limit} " - "(and not {error, enfile}) when running out of ports"]; +%% Check that accept returns {error, system_limit} +%% (and not {error, enfile}) when running out of ports. accept_system_limit(Config) when is_list(Config) -> - ?line {ok, LS} = gen_tcp:listen(0, []), - ?line {ok, TcpPort} = inet:port(LS), + {ok, LS} = gen_tcp:listen(0, []), + {ok, TcpPort} = inet:port(LS), Me = self(), - ?line Connector = spawn_link(fun () -> connector(TcpPort, Me) end), + Connector = spawn_link(fun () -> connector(TcpPort, Me) end), receive {Connector, sync} -> Connector ! {self(), continue} end, - ?line ok = acceptor(LS, false, []), - ?line Connector ! stop, + ok = acceptor(LS, false, []), + Connector ! stop, ok. acceptor(LS, GotSL, A) -> @@ -2015,132 +2501,137 @@ open_ports(L) -> end. -active_once_closed(suite) -> - []; -active_once_closed(doc) -> - ["Check that active once and tcp_close messages behave as expected"]; +%% Check that active once and tcp_close messages behave as expected. active_once_closed(Config) when is_list(Config) -> (fun() -> - ?line {Loop,A} = setup_closed_ao(), - ?line Loop({{error,closed},{error,econnaborted}}, + {Loop,A} = setup_closed_ao(), + Loop({{error,closed},{error,econnaborted}}, fun() -> gen_tcp:send(A,"Hello") end), - ?line ok = inet:setopts(A,[{active,once}]), - ?line ok = receive {tcp_closed, A} -> ok after 1000 -> error end, - ?line {error,einval} = inet:setopts(A,[{active,once}]), - ?line ok = receive {tcp_closed, A} -> error after 1000 -> ok end + ok = inet:setopts(A,[{active,once}]), + ok = receive {tcp_closed, A} -> ok after 1000 -> error end, + {error,einval} = inet:setopts(A,[{active,once}]), + ok = receive {tcp_closed, A} -> error after 1000 -> ok end end)(), (fun() -> - ?line {Loop,A} = setup_closed_ao(), - ?line Loop({{error,closed},{error,econnaborted}}, + {Loop,A} = setup_closed_ao(), + Loop({{error,closed},{error,econnaborted}}, fun() -> gen_tcp:send(A,"Hello") end), - ?line ok = inet:setopts(A,[{active,true}]), - ?line ok = receive {tcp_closed, A} -> ok after 1000 -> error end, - ?line {error,einval} = inet:setopts(A,[{active,true}]), - ?line ok = receive {tcp_closed, A} -> error after 1000 -> ok end + ok = inet:setopts(A,[{active,true}]), + ok = receive {tcp_closed, A} -> ok after 1000 -> error end, + {error,einval} = inet:setopts(A,[{active,true}]), + ok = receive {tcp_closed, A} -> error after 1000 -> ok end end)(), (fun() -> - ?line {Loop,A} = setup_closed_ao(), - ?line Loop({{error,closed},{error,econnaborted}}, + {Loop,A} = setup_closed_ao(), + Loop({{error,closed},{error,econnaborted}}, fun() -> gen_tcp:send(A,"Hello") end), - ?line ok = inet:setopts(A,[{active,true}]), - ?line ok = receive {tcp_closed, A} -> ok after 1000 -> error end, - ?line {error,einval} = inet:setopts(A,[{active,once}]), - ?line ok = receive {tcp_closed, A} -> error after 1000 -> ok end + ok = inet:setopts(A,[{active,true}]), + ok = receive {tcp_closed, A} -> ok after 1000 -> error end, + {error,einval} = inet:setopts(A,[{active,once}]), + ok = receive {tcp_closed, A} -> error after 1000 -> ok end end)(), (fun() -> - ?line {Loop,A} = setup_closed_ao(), - ?line Loop({{error,closed},{error,econnaborted}}, + {Loop,A} = setup_closed_ao(), + Loop({{error,closed},{error,econnaborted}}, fun() -> gen_tcp:send(A,"Hello") end), - ?line ok = inet:setopts(A,[{active,once}]), - ?line ok = receive {tcp_closed, A} -> ok after 1000 -> error end, - ?line {error,einval} = inet:setopts(A,[{active,true}]), - ?line ok = receive {tcp_closed, A} -> error after 1000 -> ok end + ok = inet:setopts(A,[{active,once}]), + ok = receive {tcp_closed, A} -> ok after 1000 -> error end, + {error,einval} = inet:setopts(A,[{active,true}]), + ok = receive {tcp_closed, A} -> error after 1000 -> ok end end)(), (fun() -> - ?line {Loop,A} = setup_closed_ao(), - ?line Loop({{error,closed},{error,econnaborted}}, + {Loop,A} = setup_closed_ao(), + Loop({{error,closed},{error,econnaborted}}, fun() -> gen_tcp:send(A,"Hello") end), - ?line ok = inet:setopts(A,[{active,false}]), - ?line ok = receive {tcp_closed, A} -> error after 1000 -> ok end, - ?line ok = inet:setopts(A,[{active,once}]), - ?line ok = receive {tcp_closed, A} -> ok after 1000 -> error end + ok = inet:setopts(A,[{active,false}]), + ok = receive {tcp_closed, A} -> error after 1000 -> ok end, + ok = inet:setopts(A,[{active,once}]), + ok = receive {tcp_closed, A} -> ok after 1000 -> error end end)(). -send_timeout(suite) -> - []; -send_timeout(doc) -> - ["Test the send_timeout socket option"]; +%% Test the send_timeout socket option. send_timeout(Config) when is_list(Config) -> + Dir = filename:dirname(code:which(?MODULE)), + {ok,RNode} = test_server:start_node(?UNIQ_NODE_NAME, slave, + [{args,"-pa " ++ Dir}]), + %% Basic - BasicFun = - fun(AutoClose) -> - ?line {Loop,A,RNode} = setup_timeout_sink(1000, AutoClose), - ?line {error,timeout} = - Loop(fun() -> - Res = gen_tcp:send(A,<<1:10000>>), - %%erlang:display(Res), - Res - end), - %% Check that the socket is not busy/closed... - Error = after_send_timeout(AutoClose), - ?line {error,Error} = gen_tcp:send(A,<<"Hej">>), - ?line test_server:stop_node(RNode) - end, - BasicFun(false), - BasicFun(true), - %% Check timeout length - ?line Self = self(), - ?line Pid = - spawn(fun() -> - {Loop,A,RNode} = setup_timeout_sink(1000, true), - {error,timeout} = - Loop(fun() -> - Res = gen_tcp:send(A,<<1:10000>>), - %%erlang:display(Res), + send_timeout_basic(false, RNode), + send_timeout_basic(true, RNode), + + BinData = <<1:10000>>, + + %% Check timeout length. + Self = self(), + Pid = spawn(fun() -> + A = setup_timeout_sink(RNode, 1000, true), + Send = fun() -> + Res = gen_tcp:send(A, BinData), Self ! Res, Res - end), - test_server:stop_node(RNode) - end), - ?line Diff = get_max_diff(), - ?line io:format("Max time for send: ~p~n",[Diff]), - ?line true = (Diff > 500) and (Diff < 1500), - %% Let test_server slave die... - ?line Mon = erlang:monitor(process, Pid), - ?line receive {'DOWN',Mon,process,Pid,_} -> ok end, + end, + {error,timeout} = timeout_sink_loop(Send) + end), + Diff = get_max_diff(), + io:format("Max time for send: ~p~n",[Diff]), + true = (Diff > 500) and (Diff < 1500), + + %% Wait for the process to die. + Mon = erlang:monitor(process, Pid), + receive {'DOWN',Mon,process,Pid,_} -> ok end, + %% Check that parallell writers do not hang forever - ParaFun = - fun(AutoClose) -> - ?line {Loop,A,RNode} = setup_timeout_sink(1000, AutoClose), - SenderFun = fun() -> - {error,Error} = - Loop(fun() -> - gen_tcp:send(A, <<1:10000>>) - end), - Self ! {error,Error} - end, - ?line spawn_link(SenderFun), - ?line spawn_link(SenderFun), - ?line receive - {error,timeout} -> ok - after 10000 -> - ?line exit(timeout) - end, - NextErr = after_send_timeout(AutoClose), - ?line receive - {error,NextErr} -> ok - after 10000 -> - ?line exit(timeout) - end, - ?line {error,NextErr} = gen_tcp:send(A,<<"Hej">>), - ?line test_server:stop_node(RNode) - end, - ParaFun(false), - ParaFun(true), + send_timeout_para(false, RNode), + send_timeout_para(true, RNode), + + test_server:stop_node(RNode), + + ok. + +send_timeout_basic(AutoClose, RNode) -> + BinData = <<1:10000>>, + + A = setup_timeout_sink(RNode, 1000, AutoClose), + Send = fun() -> gen_tcp:send(A, BinData) end, + {error,timeout} = timeout_sink_loop(Send), + + %% Check that the socket is not busy/closed... + Error = after_send_timeout(AutoClose), + {error,Error} = gen_tcp:send(A, <<"Hej">>), ok. + +send_timeout_para(AutoClose, RNode) -> + BinData = <<1:10000>>, + + A = setup_timeout_sink(RNode, 1000, AutoClose), + Self = self(), + SenderFun = fun() -> + Send = fun() -> gen_tcp:send(A, BinData) end, + {error,Error} = timeout_sink_loop(Send), + Self ! {error,Error} + end, + spawn_link(SenderFun), + spawn_link(SenderFun), + + receive + {error,timeout} -> ok + after 10000 -> + exit(timeout) + end, + + NextErr = after_send_timeout(AutoClose), + receive + {error,NextErr} -> ok + after 10000 -> + exit(timeout) + end, + + {error,NextErr} = gen_tcp:send(A, <<"Hej">>), + ok. + mad_sender(S) -> - {_, _, USec} = now(), - case gen_tcp:send(S, integer_to_list(USec)) of + U = rand:uniform(1000000), + case gen_tcp:send(S, integer_to_list(U)) of ok -> mad_sender(S); Err -> @@ -2151,46 +2642,41 @@ mad_sender(S) -> flush() -> receive _X -> - %erlang:display(_X), flush() after 0 -> ok end. -send_timeout_active(suite) -> - []; -send_timeout_active(doc) -> - ["Test the send_timeout socket option for active sockets"]; +%% Test the send_timeout socket option for active sockets. send_timeout_active(Config) when is_list(Config) -> - Dog = test_server:timetrap(test_server:seconds(20)), - %% Basic - BasicFun = - fun(AutoClose) -> - ?line {Loop,A,RNode,C} = setup_active_timeout_sink(1, AutoClose), - inet:setopts(A, [{active, once}]), - ?line Mad = spawn_link(RNode,fun() -> mad_sender(C) end), - ?line {error,timeout} = - Loop(fun() -> - receive - {tcp, _Sock, _Data} -> - inet:setopts(A, [{active, once}]), - Res = gen_tcp:send(A,lists:duplicate(1000, $a)), - %erlang:display(Res), - Res; - Err -> - io:format("sock closed: ~p~n", [Err]), - Err - end - end), - unlink(Mad), - exit(Mad,kill), - ?line test_server:stop_node(RNode) + Dir = filename:dirname(code:which(?MODULE)), + {ok,RNode} = test_server:start_node(?UNIQ_NODE_NAME, slave, + [{args,"-pa " ++ Dir}]), + do_send_timeout_active(false, RNode), + do_send_timeout_active(true, RNode), + test_server:stop_node(RNode), + ok. + +do_send_timeout_active(AutoClose, RNode) -> + {A,C} = setup_active_timeout_sink(RNode, 1, AutoClose), + inet:setopts(A, [{active, once}]), + Mad = spawn_link(RNode, fun() -> mad_sender(C) end), + ListData = lists:duplicate(1000, $a), + F = fun() -> + receive + {tcp, _Sock, _Data} -> + inet:setopts(A, [{active, once}]), + Res = gen_tcp:send(A, ListData), + Res; + Err -> + io:format("sock closed: ~p~n", [Err]), + Err + end end, - BasicFun(false), - flush(), - BasicFun(true), + {error,timeout} = timeout_sink_loop(F), + unlink(Mad), + exit(Mad, kill), flush(), - test_server:timetrap_cancel(Dog), ok. after_send_timeout(AutoClose) -> @@ -2208,10 +2694,10 @@ get_max_diff() -> end. get_max_diff(Max) -> - T1 = millistamp(), + T1 = millis(), receive ok -> - Diff = millistamp() - T1, + Diff = millis() - T1, if Diff > Max -> get_max_diff(Diff); @@ -2219,7 +2705,7 @@ get_max_diff(Max) -> get_max_diff(Max) end; {error,timeout} -> - Diff = millistamp() - T1, + Diff = millis() - T1, if Diff > Max -> Diff; @@ -2227,29 +2713,29 @@ get_max_diff(Max) -> Max end after 10000 -> - exit(timeout) + exit(timeout) end. setup_closed_ao() -> Dir = filename:dirname(code:which(?MODULE)), - {ok,R} = test_server:start_node(test_default_options_slave,slave, - [{args,"-pa " ++ Dir}]), - Host = list_to_atom(lists:nth(2,string:tokens(atom_to_list(node()),"@"))), + {ok,R} = test_server:start_node(?UNIQ_NODE_NAME, slave, + [{args,"-pa " ++ Dir}]), + Host = get_hostname(node()), {ok, L} = gen_tcp:listen(0, [{active,false},{packet,2}]), - Fun = fun(F) -> - receive - {From,X} when is_function(X) -> - From ! {self(),X()}, F(F); - die -> ok - end - end, + Fun = fun(F) -> + receive + {From,X} when is_function(X) -> + From ! {self(),X()}, F(F); + die -> ok + end + end, Pid = rpc:call(R,erlang,spawn,[fun() -> Fun(Fun) end]), {ok, Port} = inet:port(L), - Remote = fun(Fu) -> - Pid ! {self(), Fu}, - receive {Pid,X} -> X - end - end, + Remote = fun(Fu) -> + Pid ! {self(), Fu}, + receive {Pid,X} -> X + end + end, {ok, C} = Remote(fun() -> gen_tcp:connect(Host,Port, [{active,false},{packet,2}]) @@ -2257,113 +2743,89 @@ setup_closed_ao() -> {ok,A} = gen_tcp:accept(L), gen_tcp:send(A,"Hello"), {ok, "Hello"} = Remote(fun() -> gen_tcp:recv(C,0) end), - ok = Remote(fun() -> gen_tcp:close(C) end), - Loop2 = fun(_,_,_,0) -> + ok = Remote(fun() -> gen_tcp:close(C) end), + Loop2 = fun(_,_,_,0) -> {failure, timeout}; - (L2,{MA,MB},F2,N) -> - case F2() of - MA -> MA; - MB -> MB; - Other -> io:format("~p~n",[Other]), - receive after 1000 -> ok end, - L2(L2,{MA,MB},F2,N-1) - end + (L2,{MA,MB},F2,N) -> + case F2() of + MA -> MA; + MB -> MB; + Other -> io:format("~p~n",[Other]), + receive after 1000 -> ok end, + L2(L2,{MA,MB},F2,N-1) + end end, Loop = fun(Match2,F3) -> Loop2(Loop2,Match2,F3,10) end, test_server:stop_node(R), {Loop,A}. -setup_timeout_sink(Timeout, AutoClose) -> - ?line Dir = filename:dirname(code:which(?MODULE)), - ?line {ok,R} = test_server:start_node(test_default_options_slave,slave, - [{args,"-pa " ++ Dir}]), - ?line Host = list_to_atom(lists:nth(2,string:tokens(atom_to_list(node()),"@"))), - ?line {ok, L} = gen_tcp:listen(0, [{active,false},{packet,2}, +setup_timeout_sink(RNode, Timeout, AutoClose) -> + Host = get_hostname(node()), + {ok, L} = gen_tcp:listen(0, [{active,false},{packet,2}, {send_timeout,Timeout}, {send_timeout_close,AutoClose}]), - ?line Fun = fun(F) -> - receive - {From,X} when is_function(X) -> - From ! {self(),X()}, F(F); - die -> ok - end - end, - ?line Pid = rpc:call(R,erlang,spawn,[fun() -> Fun(Fun) end]), - ?line {ok, Port} = inet:port(L), - ?line Remote = fun(Fu) -> - Pid ! {self(), Fu}, - receive {Pid,X} -> X - end + Fun = fun(F) -> + receive + {From,X} when is_function(X) -> + From ! {self(),X()}, F(F); + die -> ok + end + end, + Pid = rpc:call(RNode, erlang, spawn, [fun() -> Fun(Fun) end]), + {ok, Port} = inet:port(L), + Remote = fun(Fu) -> + Pid ! {self(), Fu}, + receive {Pid,X} -> X + end end, - ?line {ok, C} = Remote(fun() -> + {ok, C} = Remote(fun() -> gen_tcp:connect(Host,Port, - [{active,false},{packet,2}]) + [{active,false},{packet,2}]) end), - ?line {ok,A} = gen_tcp:accept(L), - ?line gen_tcp:send(A,"Hello"), - ?line {ok, "Hello"} = Remote(fun() -> gen_tcp:recv(C,0) end), - ?line Loop2 = fun(_,_,0) -> - {failure, timeout}; - (L2,F2,N) -> - Ret = F2(), - io:format("~p~n",[Ret]), - case Ret of - ok -> receive after 1 -> ok end, - L2(L2,F2,N-1); - Other -> Other - end - end, - ?line Loop = fun(F3) -> Loop2(Loop2,F3,1000) end, - {Loop,A,R}. - -setup_active_timeout_sink(Timeout, AutoClose) -> - ?line Dir = filename:dirname(code:which(?MODULE)), - ?line {ok,R} = test_server:start_node(test_default_options_slave,slave, - [{args,"-pa " ++ Dir}]), - ?line Host = list_to_atom(lists:nth(2,string:tokens(atom_to_list(node()),"@"))), - ?line {ok, L} = gen_tcp:listen(0, [binary,{active,false},{packet,0},{nodelay, true},{keepalive, true}, - {send_timeout,Timeout}, - {send_timeout_close,AutoClose}]), - ?line Fun = fun(F) -> - receive - {From,X} when is_function(X) -> - From ! {self(),X()}, F(F); - die -> ok - end - end, - ?line Pid = rpc:call(R,erlang,spawn,[fun() -> Fun(Fun) end]), - ?line {ok, Port} = inet:port(L), - ?line Remote = fun(Fu) -> - Pid ! {self(), Fu}, - receive {Pid,X} -> X - end + {ok,A} = gen_tcp:accept(L), + gen_tcp:send(A,"Hello"), + {ok, "Hello"} = Remote(fun() -> gen_tcp:recv(C,0) end), + A. + +setup_active_timeout_sink(RNode, Timeout, AutoClose) -> + Host = get_hostname(node()), + ListenOpts = [binary,{active,false},{packet,0}, + {nodelay,true},{keepalive,true}, + {send_timeout,Timeout},{send_timeout_close,AutoClose}], + {ok, L} = gen_tcp:listen(0, ListenOpts), + Fun = fun(F) -> + receive + {From,X} when is_function(X) -> + From ! {self(),X()}, + F(F); + die -> ok + end + end, + Pid = rpc:call(RNode, erlang, spawn, [fun() -> Fun(Fun) end]), + {ok, Port} = inet:port(L), + Remote = fun(Fu) -> + Pid ! {self(), Fu}, + receive {Pid,X} -> X + end end, - ?line {ok, C} = Remote(fun() -> - gen_tcp:connect(Host,Port, - [{active,false}]) + {ok, C} = Remote(fun() -> + gen_tcp:connect(Host, Port, [{active,false}]) end), - ?line {ok,A} = gen_tcp:accept(L), - ?line gen_tcp:send(A,"Hello"), - ?line {ok, "H"++_} = Remote(fun() -> gen_tcp:recv(C,0) end), - ?line Loop2 = fun(_,_,0) -> - {failure, timeout}; - (L2,F2,N) -> - Ret = F2(), - io:format("~p~n",[Ret]), - case Ret of - ok -> receive after 1 -> ok end, - L2(L2,F2,N-1); - Other -> Other - end - end, - ?line Loop = fun(F3) -> Loop2(Loop2,F3,1000) end, - {Loop,A,R,C}. + {ok,A} = gen_tcp:accept(L), + gen_tcp:send(A, "Hello"), + {ok, "H"++_} = Remote(fun() -> gen_tcp:recv(C, 0) end), + {A,C}. +timeout_sink_loop(Action) -> + Ret = Action(), + case Ret of + ok -> + receive after 1 -> ok end, + timeout_sink_loop(Action); + Other -> + Other + end. -millistamp() -> - {Mega, Secs, Micros} = erlang:now(), - (Micros div 1000) + Secs * 1000 + Mega * 1000000000. - has_superfluous_schedulers() -> case {erlang:system_info(schedulers), erlang:system_info(logical_processors)} of @@ -2373,27 +2835,25 @@ has_superfluous_schedulers() -> end. -otp_7731(suite) -> []; -otp_7731(doc) -> - "Leaking message from inet_drv {inet_reply,P,ok} " - "when a socket sending resumes working after a send_timeout"; +%% Leaking message from inet_drv {inet_reply,P,ok} +%% when a socket sending resumes working after a send_timeout. otp_7731(Config) when is_list(Config) -> - ?line ServerPid = spawn_link(?MODULE, otp_7731_server, [self()]), - ?line receive {ServerPid, ready, PortNum} -> ok end, + ServerPid = spawn_link(?MODULE, otp_7731_server, [self()]), + receive {ServerPid, ready, PortNum} -> ok end, - ?line {ok, Socket} = gen_tcp:connect("localhost", PortNum, - [binary, {active, false}, {packet, raw}, - {send_timeout, 1000}]), + {ok, Socket} = gen_tcp:connect("localhost", PortNum, + [binary, {active, false}, {packet, raw}, + {send_timeout, 1000}]), otp_7731_send(Socket), io:format("Sending complete...\n",[]), ServerPid ! {self(), recv}, - receive {ServerPid, ok} -> ok end, - + receive {ServerPid, ok} -> ok end, + io:format("Client waiting for leaking messages...\n",[]), %% Now make sure inet_drv does not leak any internal messages. receive Msg -> - ?line test_server:fail({unexpected, Msg}) + ct:fail({unexpected, Msg}) after 1000 -> ok end, @@ -2403,15 +2863,15 @@ otp_7731(Config) when is_list(Config) -> otp_7731_send(Socket) -> Bin = <<1:10000>>, io:format("Client sending ~p bytes...\n",[size(Bin)]), - ?line case gen_tcp:send(Socket, Bin) of - ok -> otp_7731_send(Socket); - {error,timeout} -> ok - end. + case gen_tcp:send(Socket, Bin) of + ok -> otp_7731_send(Socket); + {error,timeout} -> ok + end. otp_7731_server(ClientPid) -> - ?line {ok, LSocket} = gen_tcp:listen(0, [binary, {packet, raw}, - {active, false}]), - ?line {ok, {_, PortNum}} = inet:sockname(LSocket), + {ok, LSocket} = gen_tcp:listen(0, [binary, {packet, raw}, + {active, false}]), + {ok, {_, PortNum}} = inet:sockname(LSocket), io:format("Listening on ~w with port number ~p\n", [LSocket, PortNum]), ClientPid ! {self(), ready, PortNum}, @@ -2433,7 +2893,7 @@ otp_7731_server(ClientPid) -> otp_7731_recv(Socket) -> - ?line case gen_tcp:recv(Socket, 0, 1000) of + case gen_tcp:recv(Socket, 0, 1000) of {ok, Bin} -> io:format("Server received ~p bytes\n",[size(Bin)]), otp_7731_recv(Socket); @@ -2446,27 +2906,26 @@ otp_7731_recv(Socket) -> %% OTP-7615: TCP-ports hanging in CLOSING state when sending large %% buffer followed by a recv() that returns error due to closed %% connection. -zombie_sockets(suite) -> []; -zombie_sockets(doc) -> ["OTP-7615 Leaking closed ports."]; +%% OTP-7615 Leaking closed ports. zombie_sockets(Config) when is_list(Config) -> register(zombie_collector,self()), Calls = 10, Server = spawn_link(?MODULE, zombie_server,[self(), Calls]), - ?line {Server, ready, PortNum} = receive Msg -> Msg end, + {Server, ready, PortNum} = receive Msg -> Msg end, io:format("Ports before = ~p\n",[lists:sort(erlang:ports())]), zombie_client_loop(Calls, PortNum), Ports = lists:sort(zombie_collector(Calls,[])), Server ! terminate, io:format("Collected ports = ~p\n",[Ports]), - ?line [] = zombies_alive(Ports, 10), + [] = zombies_alive(Ports, 10), timer:sleep(1000), ok. zombie_client_loop(0, _) -> ok; zombie_client_loop(N, PortNum) when is_integer(PortNum) -> - ?line {ok, Socket} = gen_tcp:connect("localhost", PortNum, - [binary, {active, false}, {packet, raw}]), - ?line gen_tcp:close(Socket), % to make server recv fail + {ok, Socket} = gen_tcp:connect("localhost", PortNum, + [binary, {active, false}, {packet, raw}]), + gen_tcp:close(Socket), % to make server recv fail zombie_client_loop(N-1, PortNum). @@ -2495,19 +2954,19 @@ zombies_alive(Ports, WaitSec) -> end. zombie_server(Pid, Calls) -> - ?line {ok, LSocket} = gen_tcp:listen(0, [binary, {packet, raw}, - {active, false}, {backlog, Calls}]), - ?line {ok, {_, PortNum}} = inet:sockname(LSocket), + {ok, LSocket} = gen_tcp:listen(0, [binary, {packet, raw}, + {active, false}, {backlog, Calls}]), + {ok, {_, PortNum}} = inet:sockname(LSocket), io:format("Listening on ~w with port number ~p\n", [LSocket, PortNum]), BigBin = list_to_binary(lists:duplicate(100*1024, 77)), Pid ! {self(), ready, PortNum}, zombie_accept_loop(LSocket, BigBin, Calls), - ?line terminate = receive Msg -> Msg end. + terminate = receive Msg -> Msg end. zombie_accept_loop(_, _, 0) -> ok; zombie_accept_loop(Socket, BigBin, Calls) -> - ?line case gen_tcp:accept(Socket) of + case gen_tcp:accept(Socket) of {ok, NewSocket} -> spawn_link(fun() -> zombie_serve_client(NewSocket, BigBin) end), zombie_accept_loop(Socket, BigBin, Calls-1); @@ -2517,29 +2976,25 @@ zombie_accept_loop(Socket, BigBin, Calls) -> zombie_serve_client(Socket, Bin) -> %%io:format("Got connection on ~p\n",[Socket]), - ?line gen_tcp:send(Socket, Bin), + gen_tcp:send(Socket, Bin), %%io:format("Sent data, waiting for reply on ~p\n",[Socket]), - ?line case gen_tcp:recv(Socket, 4) of + case gen_tcp:recv(Socket, 4) of {error,closed} -> ok; {error,econnaborted} -> ok % may be returned on Windows end, %%io:format("Closing ~p\n",[Socket]), - ?line gen_tcp:close(Socket), + gen_tcp:close(Socket), zombie_collector ! {closed, Socket}. - - -otp_7816(suite) -> []; -otp_7816(doc) -> - "Hanging send on windows when sending iolist with more than 16 binaries."; +%% Hanging send on windows when sending iolist with more than 16 binaries. otp_7816(Config) when is_list(Config) -> Client = self(), - ?line Server = spawn_link(fun()-> otp_7816_server(Client) end), - ?line receive {Server, ready, PortNum} -> ok end, + Server = spawn_link(fun()-> otp_7816_server(Client) end), + receive {Server, ready, PortNum} -> ok end, - ?line {ok, Socket} = gen_tcp:connect("localhost", PortNum, - [binary, {active, false}, {packet, 4}, - {send_timeout, 10}]), + {ok, Socket} = gen_tcp:connect("localhost", PortNum, + [binary, {active, false}, {packet, 4}, + {send_timeout, 10}]), %% We use the undocumented feature that sending can be resumed after %% a send_timeout without any data loss if the peer starts to receive data. %% Unless of course the 7816-bug is in affect, in which case the write event @@ -2549,9 +3004,9 @@ otp_7816(Config) when is_list(Config) -> io:format("Sending complete...\n",[]), - ?line ok = gen_tcp:close(Socket), + ok = gen_tcp:close(Socket), Server ! {self(), closed}, - ?line {Server, closed} = receive M -> M end. + {Server, closed} = receive M -> M end. otp_7816_send(Socket, BinNr, BinSize, Server) -> @@ -2559,7 +3014,7 @@ otp_7816_send(Socket, BinNr, BinSize, Server) -> SentBytes = otp_7816_send_data(Socket, Data, 0) * BinNr * BinSize, io:format("Client sent ~p bytes...\n",[SentBytes]), Server ! {self(),recv,SentBytes}, - ?line {Server, ok} = receive M -> M end. + {Server, ok} = receive M -> M end. @@ -2574,15 +3029,15 @@ otp_7816_send_data(Socket, Data, Loops) -> otp_7816_server(Client) -> - ?line {ok, LSocket} = gen_tcp:listen(0, [binary, {packet, 4}, + {ok, LSocket} = gen_tcp:listen(0, [binary, {packet, 4}, {active, false}]), - ?line {ok, {_, PortNum}} = inet:sockname(LSocket), + {ok, {_, PortNum}} = inet:sockname(LSocket), io:format("Listening on ~w with port number ~p\n", [LSocket, PortNum]), Client ! {self(), ready, PortNum}, - ?line {ok, CSocket} = gen_tcp:accept(LSocket), + {ok, CSocket} = gen_tcp:accept(LSocket), io:format("Server got connection...\n",[]), - ?line gen_tcp:close(LSocket), + gen_tcp:close(LSocket), otp_7816_server_loop(CSocket), @@ -2596,13 +3051,13 @@ otp_7816_server_loop(CSocket) -> {Client, recv, RecvBytes} -> io:format("Server start receiving...\n",[]), - ?line ok = otp_7816_recv(CSocket, RecvBytes), + ok = otp_7816_recv(CSocket, RecvBytes), Client ! {self(), ok}, otp_7816_server_loop(CSocket); {Client, closed} -> - ?line {error, closed} = gen_tcp:recv(CSocket, 0, 1000), + {error, closed} = gen_tcp:recv(CSocket, 0, 1000), Client ! {self(), closed} end. @@ -2611,7 +3066,7 @@ otp_7816_recv(_, 0) -> io:format("Server got all.\n",[]), ok; otp_7816_recv(CSocket, BytesLeft) -> - ?line case gen_tcp:recv(CSocket, 0, 1000) of + case gen_tcp:recv(CSocket, 0, 1000) of {ok, Bin} when byte_size(Bin) =< BytesLeft -> io:format("Server received ~p of ~p bytes.\n",[size(Bin), BytesLeft]), otp_7816_recv(CSocket, BytesLeft - byte_size(Bin)); @@ -2620,11 +3075,10 @@ otp_7816_recv(CSocket, BytesLeft) -> error end. -otp_8102(doc) -> ["Receive a packet with a faulty packet header"]; -otp_8102(suite) -> []; +%% Receive a packet with a faulty packet header. otp_8102(Config) when is_list(Config) -> - ?line {ok, LSocket} = gen_tcp:listen(0, []), - ?line {ok, {_, PortNum}} = inet:sockname(LSocket), + {ok, LSocket} = gen_tcp:listen(0, []), + {ok, {_, PortNum}} = inet:sockname(LSocket), io:format("Listening on ~w with port number ~p\n", [LSocket, PortNum]), [otp_8102_do(LSocket, PortNum, otp_8102_packet(Type,Size)) @@ -2644,92 +3098,91 @@ otp_8102_packet({cdr,little}, Size) -> otp_8102_do(LSocket, PortNum, {Bin,PType}) -> io:format("Connect with packet option ~p ...\n",[PType]), - ?line {ok, RSocket} = gen_tcp:connect("localhost", PortNum, [binary, + {ok, RSocket} = gen_tcp:connect("localhost", PortNum, [binary, {packet,PType}, {active,true}]), - ?line {ok, SSocket} = gen_tcp:accept(LSocket), + {ok, SSocket} = gen_tcp:accept(LSocket), io:format("Got connection, sending ~p...\n",[Bin]), - ?line ok = gen_tcp:send(SSocket, Bin), + ok = gen_tcp:send(SSocket, Bin), io:format("Sending complete...\n",[]), - ?line {tcp_error,RSocket,emsgsize} = receive M -> M end, + {tcp_error,RSocket,emsgsize} = receive M -> M end, io:format("Got error msg, ok.\n",[]), gen_tcp:close(SSocket), gen_tcp:close(RSocket). -otp_9389(doc) -> ["Verify packet_size handles long HTTP header lines"]; -otp_9389(suite) -> []; +%% Verify packet_size handles long HTTP header lines. otp_9389(Config) when is_list(Config) -> - ?line {ok, LS} = gen_tcp:listen(0, [{active,false}]), - ?line {ok, {_, PortNum}} = inet:sockname(LS), + {ok, LS} = gen_tcp:listen(0, [{active,false}]), + {ok, {_, PortNum}} = inet:sockname(LS), io:format("Listening on ~w with port number ~p\n", [LS, PortNum]), OrigLinkHdr = "/" ++ string:chars($S, 8192), _Server = spawn_link( fun() -> - ?line {ok, S} = gen_tcp:accept(LS), - ?line ok = inet:setopts(S, [{packet_size, 16384}]), - ?line ok = otp_9389_loop(S, OrigLinkHdr), - ?line ok = gen_tcp:close(S) + {ok, S} = gen_tcp:accept(LS), + ok = inet:setopts(S, [{packet_size, 16384}]), + ok = otp_9389_loop(S, OrigLinkHdr), + ok = gen_tcp:close(S) end), - ?line {ok, S} = gen_tcp:connect("localhost", PortNum, + {ok, S} = gen_tcp:connect("localhost", PortNum, [binary, {active, false}]), Req = "GET / HTTP/1.1\r\n" ++ "Host: localhost\r\n" ++ "Link: " ++ OrigLinkHdr ++ "\r\n\r\n", - ?line ok = gen_tcp:send(S, Req), - ?line ok = inet:setopts(S, [{packet, http}]), - ?line {ok, {http_response, {1,1}, 200, "OK"}} = gen_tcp:recv(S, 0), - ?line ok = inet:setopts(S, [{packet, httph}, {packet_size, 16384}]), - ?line {ok, {http_header, _, 'Content-Length', _, "0"}} = gen_tcp:recv(S, 0), - ?line {ok, {http_header, _, "Link", _, LinkHdr}} = gen_tcp:recv(S, 0), - ?line true = (LinkHdr == OrigLinkHdr), + ok = gen_tcp:send(S, Req), + ok = inet:setopts(S, [{packet, http}]), + {ok, {http_response, {1,1}, 200, "OK"}} = gen_tcp:recv(S, 0), + ok = inet:setopts(S, [{packet, httph}, {packet_size, 16384}]), + {ok, {http_header, _, 'Content-Length', _, "0"}} = gen_tcp:recv(S, 0), + {ok, {http_header, _, "Link", _, LinkHdr}} = gen_tcp:recv(S, 0), + true = (LinkHdr == OrigLinkHdr), ok = gen_tcp:close(S), ok = gen_tcp:close(LS), ok. otp_9389_loop(S, OrigLinkHdr) -> - ?line ok = inet:setopts(S, [{active,once},{packet,http}]), + ok = inet:setopts(S, [{active,once},{packet,http}]), receive {http, S, {http_request, 'GET', _, _}} -> - ?line ok = otp_9389_loop(S, OrigLinkHdr, undefined) + ok = otp_9389_loop(S, OrigLinkHdr, undefined) after 3000 -> - ?line error({timeout,request_line}) + error({timeout,request_line}) end. otp_9389_loop(S, OrigLinkHdr, ok) -> - ?line Resp = "HTTP/1.1 200 OK\r\nContent-length: 0\r\n" ++ + Resp = "HTTP/1.1 200 OK\r\nContent-length: 0\r\n" ++ "Link: " ++ OrigLinkHdr ++ "\r\n\r\n", - ?line ok = gen_tcp:send(S, Resp); + ok = gen_tcp:send(S, Resp); otp_9389_loop(S, OrigLinkHdr, State) -> - ?line ok = inet:setopts(S, [{active,once}, {packet,httph}]), + ok = inet:setopts(S, [{active,once}, {packet,httph}]), receive {http, S, http_eoh} -> - ?line otp_9389_loop(S, OrigLinkHdr, ok); + otp_9389_loop(S, OrigLinkHdr, ok); {http, S, {http_header, _, "Link", _, LinkHdr}} -> - ?line LinkHdr = OrigLinkHdr, - ?line otp_9389_loop(S, OrigLinkHdr, State); + LinkHdr = OrigLinkHdr, + otp_9389_loop(S, OrigLinkHdr, State); {http, S, {http_header, _, _Hdr, _, _Val}} -> - ?line otp_9389_loop(S, OrigLinkHdr, State); + otp_9389_loop(S, OrigLinkHdr, State); {http, S, {http_error, Err}} -> - ?line error({error, Err}) + error({error, Err}) after 3000 -> - ?line error({timeout,header}) + error({timeout,header}) end. -wrapping_oct(doc) -> - "Check that 64bit octet counters work."; -wrapping_oct(suite) -> - []; +wrapping_oct() -> + [{timetrap,{minutes,10}}]. + +%% Check that 64bit octet counters work. wrapping_oct(Config) when is_list(Config) -> {ok,Sock} = gen_tcp:listen(0,[{active,false},{mode,binary}]), {ok,Port} = inet:port(Sock), spawn_link(?MODULE,oct_acceptor,[Sock]), - Res = oct_datapump(Port,16#1FFFFFFFF), + Res = oct_datapump(Port,16#10000FFFF), gen_tcp:close(Sock), ok = Res, ok. @@ -2788,3 +3241,46 @@ oct_aloop(S,X,Times) -> end. ok({ok,V}) -> V. + +get_hostname(Name) -> + "@"++Host = lists:dropwhile(fun(C) -> C =/= $@ end, atom_to_list(Name)), + Host. + +otp_13939(doc) -> + ["Check that writing to a remotely closed socket doesn't block forever " + "when exit_on_close is false."]; +otp_13939(suite) -> + []; +otp_13939(Config) when is_list(Config) -> + {Pid, Ref} = spawn_opt( + fun() -> + {ok, Listener} = gen_tcp:listen(0, [{exit_on_close, false}]), + {ok, Port} = inet:port(Listener), + + spawn_link( + fun() -> + {ok, Client} = gen_tcp:connect("localhost", Port, + [{active, false}]), + ok = gen_tcp:close(Client) + end), + + {ok, Accepted} = gen_tcp:accept(Listener), + + ok = gen_tcp:send(Accepted, <<0:(10*1024*1024*8)>>), + + %% The bug surfaces when there's a delay between the send + %% operations; inet:getstat is a red herring. + timer:sleep(100), + + {error, Code} = gen_tcp:send(Accepted, <<0:(10*1024*1024*8)>>), + ct:pal("gen_tcp:send returned ~p~n", [Code]) + end, [link, monitor]), + + receive + {'DOWN', Ref, process, Pid, normal} -> + ok + after 1000 -> + demonitor(Ref, [flush]), + exit(Pid, normal), + ct:fail("Server process blocked on send.") + end. diff --git a/lib/kernel/test/gen_udp_SUITE.erl b/lib/kernel/test/gen_udp_SUITE.erl index 8177123332..af9985de45 100644 --- a/lib/kernel/test/gen_udp_SUITE.erl +++ b/lib/kernel/test/gen_udp_SUITE.erl @@ -1,32 +1,32 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2013. All Rights Reserved. +%% Copyright Ericsson AB 1998-2018. 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% -% -% test the behavior of gen_udp. Testing udp is really a very unfunny task, -% because udp is not deterministic. -% --module(gen_udp_SUITE). --include_lib("test_server/include/test_server.hrl"). +%% +%% Test the behavior of gen_udp. Testing udp is really a very unfunny task, +%% because udp is not deterministic. +%% +-module(gen_udp_SUITE). +-include_lib("common_test/include/ct.hrl"). --define(default_timeout, ?t:minutes(1)). -% XXX - we should pick a port that we _know_ is closed. That's pretty hard. +%% XXX - we should pick a port that we _know_ is closed. That's pretty hard. -define(CLOSED_PORT, 6666). -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, @@ -34,18 +34,27 @@ -export([init_per_testcase/2, end_per_testcase/2]). -export([send_to_closed/1, active_n/1, - buffer_size/1, binary_passive_recv/1, bad_address/1, - read_packets/1, open_fd/1, connect/1, implicit_inet6/1]). + buffer_size/1, binary_passive_recv/1, max_buffer_size/1, bad_address/1, + read_packets/1, open_fd/1, connect/1, implicit_inet6/1, + recvtos/1, recvtosttl/1, recvttl/1, recvtclass/1, + local_basic/1, local_unbound/1, + local_fdopen/1, local_fdopen_unbound/1, local_abstract/1]). -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap,{minutes,1}}]. all() -> - [send_to_closed, buffer_size, binary_passive_recv, + [send_to_closed, buffer_size, binary_passive_recv, max_buffer_size, bad_address, read_packets, open_fd, connect, - implicit_inet6, active_n]. + implicit_inet6, active_n, + recvtos, recvtosttl, recvttl, recvtclass, + {group, local}]. groups() -> - []. + [{local, [], + [local_basic, local_unbound, + local_fdopen, local_fdopen_unbound, local_abstract]}]. init_per_suite(Config) -> Config. @@ -53,36 +62,40 @@ init_per_suite(Config) -> end_per_suite(_Config) -> ok. +init_per_group(local, Config) -> + case gen_udp:open(0, [local]) of + {ok,S} -> + ok = gen_udp:close(S), + Config; + {error,eafnosupport} -> + {skip, "AF_LOCAL not supported"} + end; init_per_group(_GroupName, Config) -> Config. +end_per_group(local, _Config) -> + delete_local_filenames(); end_per_group(_GroupName, Config) -> Config. init_per_testcase(_Case, Config) -> - ?line Dog=test_server:timetrap(?default_timeout), - [{watchdog, Dog}|Config]. + Config. -end_per_testcase(_Case, Config) -> - Dog=?config(watchdog, Config), - test_server:timetrap_cancel(Dog), +end_per_testcase(_Case, _Config) -> ok. %%------------------------------------------------------------- %% Send two packets to a closed port (on some systems this causes the socket %% to be closed). -send_to_closed(doc) -> - ["Tests core functionality."]; -send_to_closed(suite) -> - []; +%% Tests core functionality. send_to_closed(Config) when is_list(Config) -> - ?line {ok, Sock} = gen_udp:open(0), - ?line ok = gen_udp:send(Sock, {127,0,0,1}, ?CLOSED_PORT, "foo"), + {ok, Sock} = gen_udp:open(0), + ok = gen_udp:send(Sock, {127,0,0,1}, ?CLOSED_PORT, "foo"), timer:sleep(2), - ?line ok = gen_udp:send(Sock, {127,0,0,1}, ?CLOSED_PORT, "foo"), - ?line ok = gen_udp:close(Sock), + ok = gen_udp:send(Sock, {127,0,0,1}, ?CLOSED_PORT, "foo"), + ok = gen_udp:close(Sock), ok. @@ -90,19 +103,16 @@ send_to_closed(Config) when is_list(Config) -> %%------------------------------------------------------------- %% Test that the UDP socket buffer sizes are settable -buffer_size(suite) -> - []; -buffer_size(doc) -> - ["Test UDP buffer size setting."]; +%% Test UDP buffer size setting. buffer_size(Config) when is_list(Config) -> - ?line Len = 256, - ?line Bin = list_to_binary(lists:seq(0, Len-1)), - ?line M = 8192 div Len, - ?line Spec0 = + Len = 256, + Bin = list_to_binary(lists:seq(0, Len-1)), + M = 8192 div Len, + Spec0 = [{opt,M},{safe,M-3},{long,M+1}, {opt,2*M},{safe,2*M-3},{long,2*M+1}, {opt,4*M},{safe,4*M-3},{long,4*M+1}], - ?line Spec = + Spec = [case Tag of opt -> [{recbuf,Val*Len},{sndbuf,(Val + 2)*Len}]; @@ -114,12 +124,12 @@ buffer_size(Config) when is_list(Config) -> [truncated,emsgsize,timeout]} end || {Tag,Val} <- Spec0], %% - ?line {ok, ClientSocket} = gen_udp:open(0, [binary]), - ?line {ok, ClientPort} = inet:port(ClientSocket), - ?line Client = self(), - ?line ClientIP = {127,0,0,1}, - ?line ServerIP = {127,0,0,1}, - ?line Server = + {ok, ClientSocket} = gen_udp:open(0, [binary]), + {ok, ClientPort} = inet:port(ClientSocket), + Client = self(), + ClientIP = {127,0,0,1}, + ServerIP = {127,0,0,1}, + Server = spawn_link( fun () -> {ok, ServerSocket} = gen_udp:open(0, [binary]), @@ -129,78 +139,77 @@ buffer_size(Config) when is_list(Config) -> ServerSocket, 1, Spec), ok = gen_udp:close(ServerSocket) end), - ?line Mref = erlang:monitor(process, Server), - ?line receive - {Server,port,ServerPort} -> - ?line buffer_size_client(Server, ServerIP, ServerPort, - ClientSocket, 1, Spec) - end, - ?line ok = gen_udp:close(ClientSocket), - ?line receive - {'DOWN',Mref,_,_,normal} -> - ?line ok - end. + Mref = erlang:monitor(process, Server), + receive + {Server,port,ServerPort} -> + buffer_size_client(Server, ServerIP, ServerPort, + ClientSocket, 1, Spec) + end, + ok = gen_udp:close(ClientSocket), + receive + {'DOWN',Mref,_,_,normal} -> + ok + end. buffer_size_client(_, _, _, _, _, []) -> - ?line ok; + ok; buffer_size_client(Server, IP, Port, Socket, Cnt, [Opts|T]) when is_list(Opts) -> - ?line io:format("buffer_size_client Cnt=~w setopts ~p.~n", [Cnt,Opts]), - ?line ok = inet:setopts(Socket, Opts), - ?line Server ! {self(),setopts,Cnt}, - ?line receive {Server,setopts,Cnt} -> ok end, - ?line buffer_size_client(Server, IP, Port, Socket, Cnt+1, T); + io:format("buffer_size_client Cnt=~w setopts ~p.~n", [Cnt,Opts]), + ok = inet:setopts(Socket, Opts), + Server ! {self(),setopts,Cnt}, + receive {Server,setopts,Cnt} -> ok end, + buffer_size_client(Server, IP, Port, Socket, Cnt+1, T); buffer_size_client(Server, IP, Port, Socket, Cnt, [{B,Replies}|T]=Opts) when is_binary(B) -> - ?line io:format( - "buffer_size_client Cnt=~w send size ~w expecting ~p.~n", - [Cnt,size(B),Replies]), - ?line ok = gen_udp:send(Socket, IP, Port, <<Cnt,B/binary>>), - ?line receive - {Server,Cnt,Reply} -> - ?line Tag = - if - is_tuple(Reply) -> - element(1, Reply); - is_atom(Reply) -> - Reply - end, - ?line case lists:member(Tag, Replies) of - true -> ok; - false -> - ?line - ?t:fail({reply_mismatch,Cnt,Reply,Replies, - byte_size(B), - inet:getopts(Socket, - [sndbuf,recbuf])}) - end, - ?line buffer_size_client(Server, IP, Port, Socket, Cnt+1, T) - after 1313 -> - ?line buffer_size_client(Server, IP, Port, Socket, Cnt, Opts) - end. + io:format( + "buffer_size_client Cnt=~w send size ~w expecting ~p.~n", + [Cnt,size(B),Replies]), + ok = gen_udp:send(Socket, IP, Port, <<Cnt,B/binary>>), + receive + {Server,Cnt,Reply} -> + Tag = + if + is_tuple(Reply) -> + element(1, Reply); + is_atom(Reply) -> + Reply + end, + case lists:member(Tag, Replies) of + true -> ok; + false -> + ct:fail({reply_mismatch,Cnt,Reply,Replies, + byte_size(B), + inet:getopts(Socket, + [sndbuf,recbuf])}) + end, + buffer_size_client(Server, IP, Port, Socket, Cnt+1, T) + after 1313 -> + buffer_size_client(Server, IP, Port, Socket, Cnt, Opts) + end. buffer_size_server(_, _, _, _, _, []) -> ok; buffer_size_server(Client, IP, Port, Socket, Cnt, [Opts|T]) when is_list(Opts) -> receive {Client,setopts,Cnt} -> ok end, - ?line io:format("buffer_size_server Cnt=~w setopts ~p.~n", [Cnt,Opts]), + io:format("buffer_size_server Cnt=~w setopts ~p.~n", [Cnt,Opts]), ok = inet:setopts(Socket, Opts), Client ! {self(),setopts,Cnt}, buffer_size_server(Client, IP, Port, Socket, Cnt+1, T); buffer_size_server(Client, IP, Port, Socket, Cnt, [{B,_}|T]) when is_binary(B) -> - ?line io:format( - "buffer_size_server Cnt=~w expecting size ~w.~n", - [Cnt,size(B)]), + io:format( + "buffer_size_server Cnt=~w expecting size ~w.~n", + [Cnt,size(B)]), Client ! {self(),Cnt, case buffer_size_server_recv(Socket, IP, Port, Cnt) of D when is_binary(D) -> SizeD = byte_size(D), - ?line io:format( - "buffer_size_server Cnt=~w received size ~w.~n", - [Cnt,SizeD]), + io:format( + "buffer_size_server Cnt=~w received size ~w.~n", + [Cnt,SizeD]), case B of D -> correct; @@ -210,9 +219,9 @@ buffer_size_server(Client, IP, Port, {unexpected,D} end; Error -> - ?line io:format( - "buffer_size_server Cnt=~w received error ~w.~n", - [Cnt,Error]), + io:format( + "buffer_size_server Cnt=~w received error ~w.~n", + [Cnt,Error]), Error end}, buffer_size_server(Client, IP, Port, Socket, Cnt+1, T). @@ -230,55 +239,57 @@ buffer_size_server_recv(Socket, IP, Port, Cnt) -> end. +%%------------------------------------------------------------- +%% OTP-15206: Keep buffer small for udp +%%------------------------------------------------------------- +max_buffer_size(Config) when is_list(Config) -> + {ok, Socket} = gen_udp:open(0, [binary]), + ok = inet:setopts(Socket,[{recbuf, 1 bsl 20}]), + {ok, [{buffer, 65536}]} = inet:getopts(Socket,[buffer]), + gen_udp:close(Socket). %%------------------------------------------------------------- %% OTP-3823 gen_udp:recv does not return address in binary mode %% -binary_passive_recv(suite) -> - []; -binary_passive_recv(doc) -> - ["OTP-3823 gen_udp:recv does not return address in binary mode"]; +%% OTP-3823 gen_udp:recv does not return address in binary mode. binary_passive_recv(Config) when is_list(Config) -> - ?line D1 = "The quick brown fox jumps over a lazy dog", - ?line D2 = list_to_binary(D1), - ?line D3 = ["The quick", <<" brown ">>, "fox jumps ", <<"over ">>, - <<>>, $a, [[], " lazy ", <<"dog">>]], - ?line D2 = iolist_to_binary(D3), - ?line B = D2, - ?line {ok, R} = gen_udp:open(0, [binary, {active, false}]), - ?line {ok, RP} = inet:port(R), - ?line {ok, S} = gen_udp:open(0), - ?line {ok, SP} = inet:port(S), - ?line ok = gen_udp:send(S, localhost, RP, D1), - ?line {ok, {{127, 0, 0, 1}, SP, B}} = gen_udp:recv(R, byte_size(B)+1), - ?line ok = gen_udp:send(S, localhost, RP, D2), - ?line {ok, {{127, 0, 0, 1}, SP, B}} = gen_udp:recv(R, byte_size(B)+1), - ?line ok = gen_udp:send(S, localhost, RP, D3), - ?line {ok, {{127, 0, 0, 1}, SP, B}} = gen_udp:recv(R, byte_size(B)+1), - ?line ok = gen_udp:close(S), - ?line ok = gen_udp:close(R), + D1 = "The quick brown fox jumps over a lazy dog", + D2 = list_to_binary(D1), + D3 = ["The quick", <<" brown ">>, "fox jumps ", <<"over ">>, + <<>>, $a, [[], " lazy ", <<"dog">>]], + D2 = iolist_to_binary(D3), + B = D2, + {ok, R} = gen_udp:open(0, [binary, {active, false}]), + {ok, RP} = inet:port(R), + {ok, S} = gen_udp:open(0), + {ok, SP} = inet:port(S), + ok = gen_udp:send(S, localhost, RP, D1), + {ok, {{127, 0, 0, 1}, SP, B}} = gen_udp:recv(R, byte_size(B)+1), + ok = gen_udp:send(S, localhost, RP, D2), + {ok, {{127, 0, 0, 1}, SP, B}} = gen_udp:recv(R, byte_size(B)+1), + ok = gen_udp:send(S, localhost, RP, D3), + {ok, {{127, 0, 0, 1}, SP, B}} = gen_udp:recv(R, byte_size(B)+1), + ok = gen_udp:close(S), + ok = gen_udp:close(R), ok. %%------------------------------------------------------------- %% OTP-3836 inet_udp crashes when IP-address is larger than 255. -bad_address(suite) -> - []; -bad_address(doc) -> - ["OTP-3836 inet_udp crashes when IP-address is larger than 255."]; +%% OTP-3836 inet_udp crashes when IP-address is larger than 255. bad_address(Config) when is_list(Config) -> - ?line {ok, R} = gen_udp:open(0), - ?line {ok, RP} = inet:port(R), - ?line {ok, S} = gen_udp:open(0), - ?line {ok, _SP} = inet:port(S), - ?line {'EXIT', badarg} = + {ok, R} = gen_udp:open(0), + {ok, RP} = inet:port(R), + {ok, S} = gen_udp:open(0), + {ok, _SP} = inet:port(S), + {'EXIT', badarg} = (catch gen_udp:send(S, {127,0,0,1,0}, RP, "void")), - ?line {'EXIT', badarg} = + {'EXIT', badarg} = (catch gen_udp:send(S, {127,0,0,256}, RP, "void")), - ?line ok = gen_udp:close(S), - ?line ok = gen_udp:close(R), + ok = gen_udp:close(S), + ok = gen_udp:close(R), ok. @@ -287,58 +298,55 @@ bad_address(Config) when is_list(Config) -> %% %% Starts a slave node that on command sends a bunch of messages %% to our UDP port. The receiving process just receives and -%% ignores the incoming messages, but counts them. -%% A tracing process traces the receiving process for -%% 'receive' and scheduling events. From the trace, -%% message contents is verified; and, how many messages -%% are received per in/out scheduling, which should be -%% the same as the read_packets parameter. -%% -%% What happens on the SMP emulator remains to be seen... -%% +%% ignores the incoming messages. +%% A tracing process traces the receiving port for +%% 'send' and scheduling events. From the trace, +%% how many messages are received per in/out scheduling, +%% which should never be more than the read_packet parameter. -read_packets(doc) -> - ["OTP-6249 UDP option for number of packet reads."]; +%% OTP-6249 UDP option for number of packet reads. read_packets(Config) when is_list(Config) -> - case erlang:system_info(smp_support) of - false -> - read_packets_1(); - true -> - %% We would need some new sort of tracing to test this - %% option reliably in an SMP emulator. - {skip,"SMP emulator"} - end. - -read_packets_1() -> - ?line N1 = 5, - ?line N2 = 7, - ?line {ok,R} = gen_udp:open(0, [{read_packets,N1}]), - ?line {ok,RP} = inet:port(R), - ?line {ok,Node} = start_node(gen_udp_SUITE_read_packets), - ?line Die = make_ref(), - ?line Loop = erlang:spawn_link(fun () -> infinite_loop(Die) end), + N1 = 5, + N2 = 1, + Msgs = 30000, + {ok,R} = gen_udp:open(0, [{read_packets,N1}]), + {ok,RP} = inet:port(R), + {ok,Node} = start_node(gen_udp_SUITE_read_packets), + Die = make_ref(), %% - ?line Msgs1 = [erlang:integer_to_list(M) || M <- lists:seq(1, N1*3)], - ?line [V1|_] = read_packets_test(R, RP, Msgs1, Node), - ?line {ok,[{read_packets,N1}]} = inet:getopts(R, [read_packets]), + {V1, Trace1} = read_packets_test(R, RP, Msgs, Node), + {ok,[{read_packets,N1}]} = inet:getopts(R, [read_packets]), %% - ?line ok = inet:setopts(R, [{read_packets,N2}]), - ?line Msgs2 = [erlang:integer_to_list(M) || M <- lists:seq(1, N2*3)], - ?line [V2|_] = read_packets_test(R, RP, Msgs2, Node), - ?line {ok,[{read_packets,N2}]} = inet:getopts(R, [read_packets]), + ok = inet:setopts(R, [{read_packets,N2}]), + {V2, Trace2} = read_packets_test(R, RP, Msgs, Node), + {ok,[{read_packets,N2}]} = inet:getopts(R, [read_packets]), %% - ?line stop_node(Node), - ?line Mref = erlang:monitor(process, Loop), - ?line Loop ! Die, - ?line receive - {'DOWN',Mref,_,_, normal} -> - case {V1,V2} of - {N1,N2} -> - ok; - _ when V1 =/= N1, V2 =/= N2 -> - ok - end - end. + stop_node(Node), + ct:log("N1=~p, V1=~p vs N2=~p, V2=~p",[N1,V1,N2,V2]), + + dump_terms(Config, "trace1.terms", Trace2), + dump_terms(Config, "trace2.terms", Trace2), + + %% Because of the inherit racy-ness of the feature it is + %% hard to test that it behaves correctly. + %% Right now (OTP 21) a port task takes 5% of the + %% allotted port task reductions to execute, so + %% the max number of executions a port is allowed to + %% do before being re-scheduled is N * 20 + + if + V1 > (N1 * 20) -> + ct:fail("Got ~p msgs, max was ~p", [V1, N1]); + V2 > (N2 * 20) -> + ct:fail("Got ~p msgs, max was ~p", [V2, N2]); + true -> + ok + end. + +dump_terms(Config, Name, Terms) -> + FName = filename:join(proplists:get_value(priv_dir, Config),Name), + file:write_file(FName, term_to_binary(Terms)), + ct:log("Logged terms to ~s",[FName]). infinite_loop(Die) -> receive @@ -350,7 +358,6 @@ infinite_loop(Die) -> end. read_packets_test(R, RP, Msgs, Node) -> - Len = length(Msgs), Receiver = self(), Tracer = spawn_link( @@ -375,24 +382,24 @@ read_packets_test(R, RP, Msgs, Node) -> [link,{priority,high}]), receive {Sender,{port,SP}} -> - erlang:trace(self(), true, - [running,'receive',{tracer,Tracer}]), + erlang:trace(R, true, + [running_ports,'send',{tracer,Tracer}]), erlang:yield(), Sender ! {Receiver,go}, - read_packets_recv(Len), - erlang:trace(self(), false, [all]), + read_packets_recv(Msgs), + erlang:trace(R, false, [all]), Tracer ! {Receiver,get_trace}, receive {Tracer,{trace,Trace}} -> - read_packets_verify(R, SP, Msgs, Trace) + {read_packets_verify(R, SP, Trace), Trace} end end. -read_packets_send(S, RP, [Msg|Msgs]) -> - ok = gen_udp:send(S, localhost, RP, Msg), - read_packets_send(S, RP, Msgs); -read_packets_send(_S, _RP, []) -> - ok. +read_packets_send(_S, _RP, 0) -> + ok; +read_packets_send(S, RP, Msgs) -> + ok = gen_udp:send(S, localhost, RP, "UDP FLOOOOOOD"), + read_packets_send(S, RP, Msgs - 1). read_packets_recv(0) -> ok; @@ -404,23 +411,24 @@ read_packets_recv(N) -> timeout end. -read_packets_verify(R, SP, Msg, Trace) -> - lists:reverse( - lists:sort(read_packets_verify(R, SP, Msg, Trace, 0))). - -read_packets_verify(R, SP, Msgs, [{trace,Self,OutIn,_}|Trace], M) - when Self =:= self(), OutIn =:= out; - Self =:= self(), OutIn =:= in -> - push(M, read_packets_verify(R, SP, Msgs, Trace, 0)); -read_packets_verify(R, SP, [Msg|Msgs], - [{trace,Self,'receive',{udp,R,{127,0,0,1},SP,Msg}} - |Trace], M) +read_packets_verify(R, SP, Trace) -> + [Max | _] = Pkts = lists:reverse(lists:sort(read_packets_verify(R, SP, Trace, 0))), + ct:pal("~p",[lists:sublist(Pkts,10)]), + Max. + +read_packets_verify(R, SP, [{trace,R,OutIn,_}|Trace], M) + when OutIn =:= out; OutIn =:= in -> + push(M, read_packets_verify(R, SP, Trace, 0)); +read_packets_verify(R, SP, [{trace, R,'receive',timeout}|Trace], M) -> + push(M, read_packets_verify(R, SP, Trace, 0)); +read_packets_verify(R, SP, + [{trace,R,'send',{udp,R,{127,0,0,1},SP,_Msg}, Self} | Trace], M) when Self =:= self() -> - read_packets_verify(R, SP, Msgs, Trace, M+1); -read_packets_verify(_R, _SP, [], [], M) -> + read_packets_verify(R, SP, Trace, M+1); +read_packets_verify(_R, _SP, [], M) -> push(M, []); -read_packets_verify(_R, _SP, Msgs, Trace, M) -> - ?t:fail({read_packets_verify,mismatch,Msgs,Trace,M}). +read_packets_verify(_R, _SP, Trace, M) -> + ct:fail({read_packets_verify,mismatch,Trace,M}). push(0, Vs) -> Vs; @@ -437,10 +445,7 @@ flush() -> -open_fd(suite) -> - []; -open_fd(doc) -> - ["Test that the 'fd' option works"]; +%% Test that the 'fd' option works. open_fd(Config) when is_list(Config) -> Msg = "Det gör ont när knoppar brista. Varför skulle annars våren tveka?", Addr = {127,0,0,1}, @@ -459,10 +464,10 @@ open_fd(Config) when is_list(Config) -> {udp,S3,Addr,P2,Msg} -> ok after 1000 -> - ?t:fail(io_lib:format("~w", [flush()])) + ct:fail(io_lib:format("~w", [flush()])) end after 1000 -> - ?t:fail(io_lib:format("~w", [flush()])) + ct:fail(io_lib:format("~w", [flush()])) end. active_n(Config) when is_list(Config) -> @@ -568,88 +573,390 @@ active_n(Config) when is_list(Config) -> ok = gen_udp:close(S1), ok. -% -% Utils -% + + +recvtos(_Config) -> + test_recv_opts( + inet, [{recvtos,tos,96}], + fun recvtos_ok/2). + +recvtosttl(_Config) -> + test_recv_opts( + inet, [{recvtos,tos,96},{recvttl,ttl,33}], + fun (OSType, OSVer) -> + recvtos_ok(OSType, OSVer) andalso recvttl_ok(OSType, OSVer) + end). + +recvttl(_Config) -> + test_recv_opts( + inet, [{recvttl,ttl,33}], + fun recvttl_ok/2). + +recvtclass(_Config) -> + {ok,IFs} = inet:getifaddrs(), + case + [Name || + {Name,Opts} <- IFs, + lists:member({addr,{0,0,0,0,0,0,0,1}}, Opts)] + of + [_] -> + test_recv_opts( + inet6, [{recvtclass,tclass,224}], + fun recvtclass_ok/2); + [] -> + {skip,ipv6_not_supported,IFs} + end. + +%% These version numbers are just above the highest noted in daily tests +%% where the test fails for a plausible reason, that is the lowest +%% where we can expect that the test mighe succeed, so +%% skip on platforms lower than this. +%% +%% On newer versions it might be fixed, but we'll see about that +%% when machines with newer versions gets installed... +%% If the test still fails for a plausible reason these +%% version numbers simply should be increased. +%% Or maybe we should change to only test on known good platforms? + +%% Using the option returns einval, so it is not implemented. +recvtos_ok({unix,darwin}, OSVer) -> not semver_lt(OSVer, {17,6,0}); +%% Using the option returns einval, so it is not implemented. +recvtos_ok({unix,openbsd}, OSVer) -> not semver_lt(OSVer, {6,4,0}); +%% Using the option returns einval, so it is not implemented. +recvtos_ok({unix,sunos}, OSVer) -> not semver_lt(OSVer, {5,12,0}); +%% +recvtos_ok({unix,_}, _) -> true; +recvtos_ok(_, _) -> false. + +recvttl_ok({unix,_}, _) -> true; +recvttl_ok(_, _) -> false. + +%% Using the option returns einval, so it is not implemented. +recvtclass_ok({unix,darwin}, OSVer) -> not semver_lt(OSVer, {9,9,0}); +recvtclass_ok({unix,linux}, OSVer) -> not semver_lt(OSVer, {2,6,11}); +%% +recvtclass_ok({unix,_}, _) -> true; +recvtclass_ok(_, _) -> false. + +semver_lt({X1,Y1,Z1}, {X2,Y2,Z2}) -> + if + X1 > X2 -> false; + X1 < X2 -> true; + Y1 > Y2 -> false; + Y1 < Y2 -> true; + Z1 > Z2 -> false; + Z1 < Z2 -> true; + true -> false + end; +semver_lt(_, {_,_,_}) -> false. + +test_recv_opts(Family, Spec, OSFilter) -> + OSType = os:type(), + OSVer = os:version(), + case OSFilter(OSType, OSVer) of + true -> + io:format("Os: ~p, ~p~n", [OSType,OSVer]), + test_recv_opts(Family, Spec, OSType, OSVer); + false -> + {skip,{not_supported_for_os_version,{OSType,OSVer}}} + end. +%% +test_recv_opts(Family, Spec, _OSType, _OSVer) -> + Timeout = 5000, + RecvOpts = [RecvOpt || {RecvOpt,_,_} <- Spec], + TrueRecvOpts = [{RecvOpt,true} || {RecvOpt,_,_} <- Spec], + FalseRecvOpts = [{RecvOpt,false} || {RecvOpt,_,_} <- Spec], + Opts = [Opt || {_,Opt,_} <- Spec], + OptsVals = [{Opt,Val} || {_,Opt,Val} <- Spec], + TrueRecvOpts_OptsVals = TrueRecvOpts ++ OptsVals, + Addr = + case Family of + inet -> + {127,0,0,1}; + inet6 -> + {0,0,0,0,0,0,0,1} + end, + %% + {ok,S1} = + gen_udp:open(0, [Family,binary,{active,false}|TrueRecvOpts]), + {ok,P1} = inet:port(S1), + {ok,TrueRecvOpts} = inet:getopts(S1, RecvOpts), + ok = inet:setopts(S1, FalseRecvOpts), + {ok,FalseRecvOpts} = inet:getopts(S1, RecvOpts), + ok = inet:setopts(S1, TrueRecvOpts_OptsVals), + {ok,TrueRecvOpts_OptsVals} = inet:getopts(S1, RecvOpts ++ Opts), + %% + {ok,S2} = + gen_udp:open(0, [Family,binary,{active,true}|FalseRecvOpts]), + {ok,P2} = inet:port(S2), + {ok,FalseRecvOpts_OptsVals2} = inet:getopts(S2, RecvOpts ++ Opts), + OptsVals2 = FalseRecvOpts_OptsVals2 -- FalseRecvOpts, + %% + ok = gen_udp:send(S2, Addr, P1, <<"abcde">>), + ok = gen_udp:send(S1, Addr, P2, <<"fghij">>), + {ok,{_,P2,OptsVals3,<<"abcde">>}} = gen_udp:recv(S1, 0, Timeout), + verify_sets_eq(OptsVals3, OptsVals2), + receive + {udp,S2,_,P1,<<"fghij">>} -> + ok; + Other1 -> + exit({unexpected,Other1}) + after Timeout -> + exit(timeout) + end, + %% + ok = inet:setopts(S1, FalseRecvOpts), + {ok,FalseRecvOpts} = inet:getopts(S1, RecvOpts), + ok = inet:setopts(S2, TrueRecvOpts), + {ok,TrueRecvOpts} = inet:getopts(S2, RecvOpts), + %% + ok = gen_udp:send(S2, Addr, P1, <<"klmno">>), + ok = gen_udp:send(S1, Addr, P2, <<"pqrst">>), + {ok,{_,P2,<<"klmno">>}} = gen_udp:recv(S1, 0, Timeout), + receive + {udp,S2,_,P1,OptsVals4,<<"pqrst">>} -> + verify_sets_eq(OptsVals4, OptsVals); + Other2 -> + exit({unexpected,Other2}) + after Timeout -> + exit(timeout) + end, + ok = gen_udp:close(S1), + ok = gen_udp:close(S2), +%% exit({{OSType,OSVer},success}), % In search for the truth + ok. + +verify_sets_eq(L1, L2) -> + L = lists:sort(L1), + case lists:sort(L2) of + L -> + ok; + _ -> + exit({sets_neq,L1,L2}) + end. + + +local_basic(_Config) -> + SFile = local_filename(server), + SAddr = {local,bin_filename(SFile)}, + CFile = local_filename(client), + CAddr = {local,bin_filename(CFile)}, + _ = file:delete(SFile), + _ = file:delete(CFile), + %% + S = ok(gen_udp:open(0, [{ifaddr,{local,SFile}},{active,false}])), + C = ok(gen_udp:open(0, [{ifaddr,{local,CFile}},{active,false}])), + SAddr = ok(inet:sockname(S)), + CAddr = ok(inet:sockname(C)), + local_handshake(S, SAddr, C, CAddr), + ok = gen_udp:close(S), + ok = gen_udp:close(C), + %% + ok = file:delete(SFile), + ok = file:delete(CFile), + ok. + +local_unbound(_Config) -> + SFile = local_filename(server), + SAddr = {local,bin_filename(SFile)}, + _ = file:delete(SFile), + %% + S = ok(gen_udp:open(0, [{ifaddr,SAddr},{active,false}])), + C = ok(gen_udp:open(0, [local,{active,false}])), + SAddr = ok(inet:sockname(S)), + local_handshake(S, SAddr, C, undefined), + ok = gen_udp:close(S), + ok = gen_udp:close(C), + %% + ok = file:delete(SFile), + ok. + +local_fdopen(_Config) -> + SFile = local_filename(server), + SAddr = {local,bin_filename(SFile)}, + CFile = local_filename(client), + CAddr = {local,bin_filename(CFile)}, + _ = file:delete(SFile), + _ = file:delete(CFile), + %% + S0 = ok(gen_udp:open(0, [{ifaddr,SAddr},{active,false}])), + C = ok(gen_udp:open(0, [{ifaddr,{local,CFile}},{active,false}])), + SAddr = ok(inet:sockname(S0)), + CAddr = ok(inet:sockname(C)), + Fd = ok(prim_inet:getfd(S0)), + S = ok(gen_udp:open(0, [{fd,Fd},local,{active,false}])), + SAddr = ok(inet:sockname(S)), + local_handshake(S, SAddr, C, CAddr), + ok = gen_udp:close(S), + ok = gen_udp:close(S0), + ok = gen_udp:close(C), + %% + ok = file:delete(SFile), + ok = file:delete(CFile), + ok. + +local_fdopen_unbound(_Config) -> + SFile = local_filename(server), + SAddr = {local,bin_filename(SFile)}, + _ = file:delete(SFile), + %% + S = ok(gen_udp:open(0, [{ifaddr,SAddr},{active,false}])), + C0 = ok(gen_udp:open(0, [local,{active,false}])), + SAddr = ok(inet:sockname(S)), + Fd = ok(prim_inet:getfd(C0)), + C = ok(gen_udp:open(0, [{fd,Fd},local,{active,false}])), + local_handshake(S, SAddr, C, undefined), + ok = gen_udp:close(S), + ok = gen_udp:close(C), + ok = gen_udp:close(C0), + %% + ok = file:delete(SFile), + ok. + +local_abstract(_Config) -> + case os:type() of + {unix,linux} -> + S = ok(gen_udp:open(0, [{ifaddr,{local,<<>>}},{active,false}])), + C = ok(gen_udp:open(0, [{ifaddr,{local,<<>>}},{active,false}])), + {local,_} = SAddr = ok(inet:sockname(S)), + {local,_} = CAddr = ok(inet:sockname(C)), + local_handshake(S, SAddr, C, CAddr), + ok = gen_udp:close(S), + ok = gen_udp:close(C), + ok; + _ -> + {skip,"AF_LOCAL Abstract Addresses only supported on Linux"} + end. + + +local_handshake(S, SAddr, C, CAddr) -> + SData = "9876543210", + CData = "0123456789", + ok = gen_udp:send(C, SAddr, 0, CData), + case ok(gen_tcp:recv(S, 112)) of + {{unspec,<<>>}, 0, CData} when CAddr =:= undefined -> + ok; + {{local,<<>>}, 0, CData} when CAddr =:= undefined -> + ok; + {CAddr, 0, CData} when CAddr =/= undefined -> + ok = gen_udp:send(S, CAddr, 0, SData), + {SAddr, 0, SData} = ok(gen_tcp:recv(C, 112)), + ok + + end. + +%% +%% Utils +%% + start_node(Name) -> Pa = filename:dirname(code:which(?MODULE)), - ?t:start_node(Name, slave, [{args, "-pa " ++ Pa}]). + test_server:start_node(Name, slave, [{args, "-pa " ++ Pa}]). stop_node(Node) -> - ?t:stop_node(Node). + test_server:stop_node(Node). -connect(suite) -> - []; -connect(doc) -> - ["Test that connect/3 has effect"]; +%% Test that connect/3 has effect. connect(Config) when is_list(Config) -> - ?line Addr = {127,0,0,1}, - ?line {ok,S1} = gen_udp:open(0), - ?line {ok,P1} = inet:port(S1), - ?line {ok,S2} = gen_udp:open(0), - ?line ok = inet:setopts(S2, [{active,false}]), - ?line ok = gen_udp:close(S1), - ?line ok = gen_udp:connect(S2, Addr, P1), - ?line ok = gen_udp:send(S2, <<16#deadbeef:32>>), - ?line ok = case gen_udp:recv(S2, 0, 5) of - {error,econnrefused} -> ok; - {error,econnreset} -> ok; - Other -> Other - end, + Addr = {127,0,0,1}, + {ok,S1} = gen_udp:open(0), + {ok,P1} = inet:port(S1), + {ok,S2} = gen_udp:open(0), + ok = inet:setopts(S2, [{active,false}]), + ok = gen_udp:close(S1), + ok = gen_udp:connect(S2, Addr, P1), + ok = gen_udp:send(S2, <<16#deadbeef:32>>), + ok = case gen_udp:recv(S2, 0, 500) of + {error,econnrefused} -> ok; + {error,econnreset} -> ok; + Other -> Other + end, ok. implicit_inet6(Config) when is_list(Config) -> - ?line Host = ok(inet:gethostname()), - ?line - case inet:getaddr(Host, inet6) of - {ok,Addr} -> - ?line implicit_inet6(Host, Addr); - {error,Reason} -> - {skip, - "Can not look up IPv6 address: " - ++atom_to_list(Reason)} - end. + Host = ok(inet:gethostname()), + case inet:getaddr(Host, inet6) of + {ok,Addr} -> + implicit_inet6(Host, Addr); + {error,Reason} -> + {skip, + "Can not look up IPv6 address: " + ++atom_to_list(Reason)} + end. implicit_inet6(Host, Addr) -> - ?line Active = {active,false}, - ?line - case gen_udp:open(0, [inet6,Active]) of - {ok,S1} -> - ?line Loopback = {0,0,0,0,0,0,0,1}, - ?line io:format("~s ~p~n", ["::1",Loopback]), - ?line implicit_inet6(S1, Active, Loopback), - ?line ok = gen_udp:close(S1), - %% - ?line Localhost = "localhost", - ?line Localaddr = ok(inet:getaddr(Localhost, inet6)), - ?line io:format("~s ~p~n", [Localhost,Localaddr]), - ?line S2 = ok(gen_udp:open(0, [{ip,Localaddr},Active])), - ?line implicit_inet6(S2, Active, Localaddr), - ?line ok = gen_udp:close(S2), - %% - ?line io:format("~s ~p~n", [Host,Addr]), - ?line S3 = ok(gen_udp:open(0, [{ifaddr,Addr},Active])), - ?line implicit_inet6(S3, Active, Addr), - ?line ok = gen_udp:close(S3); - _ -> - {skip,"IPv6 not supported"} - end. + Active = {active,false}, + Loopback = {0,0,0,0,0,0,0,1}, + case gen_udp:open(0, [inet6,Active,{ip, Loopback}]) of + {ok,S1} -> + io:format("~s ~p~n", ["::1",Loopback]), + implicit_inet6(S1, Active, Loopback), + ok = gen_udp:close(S1), + %% + Localaddr = ok(get_localaddr()), + S2 = ok(gen_udp:open(0, [{ip,Localaddr},Active])), + implicit_inet6(S2, Active, Localaddr), + ok = gen_udp:close(S2), + %% + io:format("~s ~p~n", [Host,Addr]), + S3 = ok(gen_udp:open(0, [{ifaddr,Addr},Active])), + implicit_inet6(S3, Active, Addr), + ok = gen_udp:close(S3); + _ -> + {skip,"IPv6 not supported"} + end. implicit_inet6(S1, Active, Addr) -> - ?line P1 = ok(inet:port(S1)), - ?line S2 = ok(gen_udp:open(0, [inet6,Active])), - ?line P2 = ok(inet:port(S2)), - ?line ok = gen_udp:connect(S2, Addr, P1), - ?line ok = gen_udp:connect(S1, Addr, P2), - ?line {Addr,P2} = ok(inet:peername(S1)), - ?line {Addr,P1} = ok(inet:peername(S2)), - ?line {Addr,P1} = ok(inet:sockname(S1)), - ?line {Addr,P2} = ok(inet:sockname(S2)), - ?line ok = gen_udp:send(S1, Addr, P2, "ping"), - ?line {Addr,P1,"ping"} = ok(gen_udp:recv(S2, 1024, 1000)), - ?line ok = gen_udp:send(S2, Addr, P1, "pong"), - ?line {Addr,P2,"pong"} = ok(gen_udp:recv(S1, 1024)), - ?line ok = gen_udp:close(S2). - -ok({ok,V}) -> V. + P1 = ok(inet:port(S1)), + S2 = ok(gen_udp:open(0, [inet6,Active])), + P2 = ok(inet:port(S2)), + ok = gen_udp:connect(S2, Addr, P1), + ok = gen_udp:connect(S1, Addr, P2), + {Addr,P2} = ok(inet:peername(S1)), + {Addr,P1} = ok(inet:peername(S2)), + {Addr,P1} = ok(inet:sockname(S1)), + {Addr,P2} = ok(inet:sockname(S2)), + ok = gen_udp:send(S1, Addr, P2, "ping"), + {Addr,P1,"ping"} = ok(gen_udp:recv(S2, 1024, 1000)), + ok = gen_udp:send(S2, Addr, P1, "pong"), + {Addr,P2,"pong"} = ok(gen_udp:recv(S1, 1024)), + ok = gen_udp:close(S2). + +ok({ok,V}) -> V; +ok(NotOk) -> + try throw(not_ok) + catch + throw:Thrown:Stacktrace -> + erlang:raise( + error, {Thrown, NotOk}, tl(Stacktrace)) + end. + + +local_filename(Tag) -> + "/tmp/" ?MODULE_STRING "_" ++ os:getpid() ++ "_" ++ atom_to_list(Tag). + +bin_filename(String) -> + unicode:characters_to_binary(String, file:native_name_encoding()). + +delete_local_filenames() -> + _ = + [file:delete(F) || + F <- + filelib:wildcard( + "/tmp/" ?MODULE_STRING "_" ++ os:getpid() ++ "_*")], + ok. + +get_localaddr() -> + get_localaddr(["localhost", "localhost6", "ip6-localhost"]). + +get_localaddr([]) -> + {error, localaddr_not_found}; +get_localaddr([Localhost|Ls]) -> + case inet:getaddr(Localhost, inet6) of + {ok, LocalAddr} -> + io:format("~s ~p~n", [Localhost, LocalAddr]), + {ok, LocalAddr}; + _ -> + get_localaddr(Ls) + end. diff --git a/lib/kernel/test/global_SUITE.erl b/lib/kernel/test/global_SUITE.erl index 9428a38660..8eab36e308 100644 --- a/lib/kernel/test/global_SUITE.erl +++ b/lib/kernel/test/global_SUITE.erl @@ -1,25 +1,24 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2012. All Rights Reserved. +%% Copyright Ericsson AB 1997-2018. 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/. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at %% -%% 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. +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% -module(global_SUITE). -%-define(line_trace, 1). - -export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2, init_per_suite/1, end_per_suite/1, names/1, names_hidden/1, locks/1, locks_hidden/1, @@ -50,7 +49,7 @@ -compile(export_all). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -define(NODES, [node()|nodes()]). @@ -60,7 +59,8 @@ -define(GLOBAL_LOCK, global). -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}]. all() -> case init:get_argument(ring_line) of @@ -85,10 +85,10 @@ groups() -> ring]}]. init_per_group(_GroupName, Config) -> - Config. + Config. end_per_group(_GroupName, Config) -> - Config. + Config. init_per_suite(Config) -> Config. @@ -98,9 +98,9 @@ end_per_suite(_Config) -> -define(TESTCASE, testcase_name). --define(testcase, ?config(?TESTCASE, Config)). +-define(testcase, proplists:get_value(?TESTCASE, Config)). -define(nodes_tag, '$global_nodes'). --define(registered, ?config(registered, Config)). +-define(registered, proplists:get_value(registered, Config)). init_per_testcase(Case, Config) when is_atom(Case), is_list(Config) -> ok = gen_server:call(global_name_server, high_level_trace_start,infinity), @@ -114,16 +114,16 @@ init_per_testcase(Case, Config) when is_atom(Case), is_list(Config) -> end_per_testcase(_Case, Config) -> ct:log("Calling end_per_testcase!",[]), - ?line write_high_level_trace(Config), - ?line _ = + write_high_level_trace(Config), + _ = gen_server:call(global_name_server, high_level_trace_stop, infinity), [global:unregister_name(N) || N <- global:registered_names()], - ?line InitRegistered = ?registered, - ?line Registered = registered(), - ?line [io:format("~s local names: ~p~n", [What, N]) || - {What, N} <- [{"Added", Registered -- InitRegistered}, - {"Removed", InitRegistered -- Registered}], - N =/= []], + InitRegistered = ?registered, + Registered = registered(), + [io:format("~s local names: ~p~n", [What, N]) || + {What, N} <- [{"Added", Registered -- InitRegistered}, + {"Removed", InitRegistered -- Registered}], + N =/= []], ok. @@ -146,12 +146,11 @@ end_per_testcase(_Case, Config) -> %%% and releases the lock. Now the name should exist on both our own node %%% and on the slave node (we wait until that is true; it seems that we %%% can do rpc calls to another node before the connection is really up). -register_1(suite) -> []; register_1(Config) when is_list(Config) -> Timeout = 15, ct:timetrap({seconds,Timeout}), init_high_level_trace(Timeout), - ?line init_condition(Config), + init_condition(Config), P = spawn_link(?MODULE, lock_global, [self(), Config]), receive {P, ok} -> @@ -160,7 +159,7 @@ register_1(Config) when is_list(Config) -> end, P ! step2, io:format("p1: sent step2~n"), - ?line yes = global:register_name(foo, self()), + yes = global:register_name(foo, self()), io:format("p1: registered~n"), P ! step3, receive @@ -171,11 +170,11 @@ register_1(Config) when is_list(Config) -> I =:= I2 -> ok; true -> - test_server:fail({notsync, I, I2}) + ct:fail({notsync, I, I2}) end, - ?line _ = global:unregister_name(foo), + _ = global:unregister_name(foo), write_high_level_trace(Config), - ?line init_condition(Config), + init_condition(Config), ok. lock_global(Parent, Config) -> @@ -202,7 +201,7 @@ lock_global(Parent, Config) -> io:format("p2: received step3~n"), I = global:whereis_name(foo), io:format("p2: name ~p~n", [I]), - ?line ?UNTIL(I =:= rpc:call(N1, global, whereis_name, [foo])), + ?UNTIL(I =:= rpc:call(N1, global, whereis_name, [foo])), I2 = I, slave:stop(N1), io:format("p2: name2 ~p~n", [I2]), @@ -215,75 +214,73 @@ lock_global(Parent, Config) -> %%% 'try_again_locker' would be called, and this time cause both 1 and 2 %%% to obtain a lock for 'global' on node 3, which would keep the %%% name registry from ever becoming consistent again. -both_known_1(suite) -> []; both_known_1(Config) when is_list(Config) -> Timeout = 30, ct:timetrap({seconds,Timeout}), init_high_level_trace(Timeout), - ?line init_condition(Config), + init_condition(Config), - ?line OrigNames = global:registered_names(), + OrigNames = global:registered_names(), - ?line [Cp1, Cp2, Cp3] = start_nodes([cp1, cp2, cp3], slave, Config), + [Cp1, Cp2, Cp3] = start_nodes([cp1, cp2, cp3], slave, Config), - ?line wait_for_ready_net(Config), + wait_for_ready_net(Config), - ?line rpc_disconnect_node(Cp1, Cp2, Config), + rpc_disconnect_node(Cp1, Cp2, Config), - ?line {_Pid1, yes} = rpc:call(Cp1, ?MODULE, start_proc, [p1]), - ?line {_Pid2, yes} = rpc:call(Cp2, ?MODULE, start_proc, [p2]), + {_Pid1, yes} = rpc:call(Cp1, ?MODULE, start_proc, [p1]), + {_Pid2, yes} = rpc:call(Cp2, ?MODULE, start_proc, [p2]), - ?line Names10 = rpc:call(Cp1, global, registered_names, []), - ?line Names20 = rpc:call(Cp2, global, registered_names, []), - ?line Names30 = rpc:call(Cp3, global, registered_names, []), + Names10 = rpc:call(Cp1, global, registered_names, []), + Names20 = rpc:call(Cp2, global, registered_names, []), + Names30 = rpc:call(Cp3, global, registered_names, []), Names1 = Names10 -- OrigNames, Names2 = Names20 -- OrigNames, Names3 = Names30 -- OrigNames, - ?line [p1] = lists:sort(Names1), - ?line [p2] = lists:sort(Names2), - ?line [p1, p2] = lists:sort(Names3), + [p1] = lists:sort(Names1), + [p2] = lists:sort(Names2), + [p1, p2] = lists:sort(Names3), - ?line Locker = spawn(Cp3, ?MODULE, lock_global2, [{global, l3}, - self()]), + Locker = spawn(Cp3, ?MODULE, lock_global2, [{global, l3}, + self()]), - ?line receive - {locked, S} -> - true = S - end, + receive + {locked, S} -> + true = S + end, - ?line pong = rpc:call(Cp1, net_adm, ping, [Cp2]), + pong = rpc:call(Cp1, net_adm, ping, [Cp2]), %% Bring cp1 and cp2 together, while someone has locked global. %% They will now loop in 'loop_locker'. - ?line Names10_2 = rpc:call(Cp1, global, registered_names, []), - ?line Names20_2 = rpc:call(Cp2, global, registered_names, []), - ?line Names30_2 = rpc:call(Cp3, global, registered_names, []), + Names10_2 = rpc:call(Cp1, global, registered_names, []), + Names20_2 = rpc:call(Cp2, global, registered_names, []), + Names30_2 = rpc:call(Cp3, global, registered_names, []), Names1_2 = Names10_2 -- OrigNames, Names2_2 = Names20_2 -- OrigNames, Names3_2 = Names30_2 -- OrigNames, - ?line [p1] = lists:sort(Names1_2), - ?line [p2] = lists:sort(Names2_2), - ?line [p1, p2] = lists:sort(Names3_2), + [p1] = lists:sort(Names1_2), + [p2] = lists:sort(Names2_2), + [p1, p2] = lists:sort(Names3_2), %% Let go of the lock, and expect the lockers to resolve the name %% registry. Locker ! {ok, self()}, - ?line ?UNTIL(begin - ?line Names10_3 = rpc:call(Cp1, global, registered_names, []), - ?line Names20_3 = rpc:call(Cp2, global, registered_names, []), - ?line Names30_3 = rpc:call(Cp3, global, registered_names, []), - + Names10_3 = rpc:call(Cp1, global, registered_names, []), + Names20_3 = rpc:call(Cp2, global, registered_names, []), + Names30_3 = rpc:call(Cp3, global, registered_names, []), + Names1_3 = Names10_3 -- OrigNames, Names2_3 = Names20_3 -- OrigNames, Names3_3 = Names30_3 -- OrigNames, - + N1 = lists:sort(Names1_3), N2 = lists:sort(Names2_3), N3 = lists:sort(Names3_3), @@ -295,51 +292,49 @@ both_known_1(Config) when is_list(Config) -> stop_node(Cp2), stop_node(Cp3), - ?line init_condition(Config), + init_condition(Config), ok. -lost_unregister(suite) -> []; -lost_unregister(doc) -> - ["OTP-6428. An unregistered name reappears."]; +%% OTP-6428. An unregistered name reappears. lost_unregister(Config) when is_list(Config) -> Timeout = 30, ct:timetrap({seconds,Timeout}), init_high_level_trace(Timeout), - ?line init_condition(Config), + init_condition(Config), - ?line {ok, B} = start_node(b, Config), - ?line {ok, C} = start_node(c, Config), + {ok, B} = start_node(b, Config), + {ok, C} = start_node(c, Config), Nodes = [node(), B, C], - ?line wait_for_ready_net(Config), + wait_for_ready_net(Config), - % start a proc and register it - ?line {Pid, yes} = start_proc(test), + %% start a proc and register it + {Pid, yes} = start_proc(test), - ?line ?UNTIL(Pid =:= global:whereis_name(test)), - ?line check_everywhere(Nodes, test, Config), + ?UNTIL(Pid =:= global:whereis_name(test)), + check_everywhere(Nodes, test, Config), - ?line rpc_disconnect_node(B, C, Config), - ?line check_everywhere(Nodes, test, Config), - ?line _ = rpc:call(B, global, unregister_name, [test]), - ?line ?UNTIL(undefined =:= global:whereis_name(test)), - ?line Pid = rpc:call(C, global, whereis_name, [test]), - ?line check_everywhere(Nodes--[C], test, Config), - ?line pong = rpc:call(B, net_adm, ping, [C]), + rpc_disconnect_node(B, C, Config), + check_everywhere(Nodes, test, Config), + _ = rpc:call(B, global, unregister_name, [test]), + ?UNTIL(undefined =:= global:whereis_name(test)), + Pid = rpc:call(C, global, whereis_name, [test]), + check_everywhere(Nodes--[C], test, Config), + pong = rpc:call(B, net_adm, ping, [C]), %% Now the name has reappeared on node B. - ?line ?UNTIL(Pid =:= global:whereis_name(test)), - ?line check_everywhere(Nodes, test, Config), + ?UNTIL(Pid =:= global:whereis_name(test)), + check_everywhere(Nodes, test, Config), exit_p(Pid), - ?line ?UNTIL(undefined =:= global:whereis_name(test)), - ?line check_everywhere(Nodes, test, Config), + ?UNTIL(undefined =:= global:whereis_name(test)), + check_everywhere(Nodes, test, Config), write_high_level_trace(Config), stop_node(B), stop_node(C), - ?line init_condition(Config), + init_condition(Config), ok. -define(UNTIL_LOOP, 300). @@ -349,7 +344,7 @@ lost_unregister(Config) when is_list(Config) -> init_high_level_trace(Time) -> Mul = try test_server:timetrap_scale_factor() - catch _:_ -> 1 + catch _:_ -> 1 end, put(?end_tag, msec() + Time * Mul * 1000), %% Assures that started nodes start the high level trace automatically. @@ -394,7 +389,7 @@ write_high_level_trace(Nodes, Config) -> %% 'info' returns more than the trace, which is nice. Data = [{Node, {info, rpc:call(Node, global, info, [])}} || Node <- Nodes], - Dir = ?config(priv_dir, Config), + Dir = proplists:get_value(priv_dir, Config), DataFile = filename:join([Dir, lists:concat(["global_", ?testcase])]), file:write_file(DataFile, term_to_binary({high_level_trace, When, Data})). @@ -412,37 +407,35 @@ lock_global2(Id, Parent) -> %% erl -sname XXX -rsh ctrsh where XX not in [cp1, cp2, cp3] %%----------------------------------------------------------------- -%cp1 - cp3 are started, and the name 'test' registered for a process on -%test_server. Then it is checked that the name is registered on all -%nodes, using whereis_name. Check that the same -%name can't be registered with another value. Exit the registered -%process and check that the name disappears. Register a new process -%(Pid2) under the name 'test'. Let another new process (Pid3) -%reregister itself under the same name. Test global:send/2. Test -%unregister. Kill Pid3. Start a process (Pid6) on cp3, -%register it as 'test', stop cp1 - cp3 and check that 'test' disappeared. -%Kill Pid2 and check that 'test' isn't registered. - -names(suite) -> []; +%% cp1 - cp3 are started, and the name 'test' registered for a process on +%% test_server. Then it is checked that the name is registered on all +%% nodes, using whereis_name. Check that the same +%% name can't be registered with another value. Exit the registered +%% process and check that the name disappears. Register a new process +%% (Pid2) under the name 'test'. Let another new process (Pid3) +%% reregister itself under the same name. Test global:send/2. Test +%% unregister. Kill Pid3. Start a process (Pid6) on cp3, +%% register it as 'test', stop cp1 - cp3 and check that 'test' disappeared. +%% Kill Pid2 and check that 'test' isn't registered. + names(Config) when is_list(Config) -> Timeout = 30, ct:timetrap({seconds,Timeout}), init_high_level_trace(Timeout), - ?line init_condition(Config), - ?line OrigNames = global:registered_names(), + init_condition(Config), + OrigNames = global:registered_names(), - ?line {ok, Cp1} = start_node(cp1, Config), - ?line {ok, Cp2} = start_node(cp2, Config), - ?line {ok, Cp3} = start_node(cp3, Config), + {ok, Cp1} = start_node(cp1, Config), + {ok, Cp2} = start_node(cp2, Config), + {ok, Cp3} = start_node(cp3, Config), - ?line wait_for_ready_net(Config), + wait_for_ready_net(Config), - % start a proc and register it - ?line {Pid, yes} = start_proc(test), + %% start a proc and register it + {Pid, yes} = start_proc(test), - % test that it is registered at all nodes - ?line - ?UNTIL(begin + %% test that it is registered at all nodes + ?UNTIL(begin (Pid =:= global:whereis_name(test)) and (Pid =:= rpc:call(Cp1, global, whereis_name, [test])) and (Pid =:= rpc:call(Cp2, global, whereis_name, [test])) and @@ -450,156 +443,148 @@ names(Config) when is_list(Config) -> ([test] =:= global:registered_names() -- OrigNames) end), - % try to register the same name - ?line no = global:register_name(test, self()), - ?line no = rpc:call(Cp1, global, register_name, [test, self()]), + %% try to register the same name + no = global:register_name(test, self()), + no = rpc:call(Cp1, global, register_name, [test, self()]), - % let process exit, check that it is unregistered automatically + %% let process exit, check that it is unregistered automatically exit_p(Pid), - ?line - ?UNTIL((undefined =:= global:whereis_name(test)) and + ?UNTIL((undefined =:= global:whereis_name(test)) and (undefined =:= rpc:call(Cp1, global, whereis_name, [test])) and (undefined =:= rpc:call(Cp2, global, whereis_name, [test])) and (undefined =:= rpc:call(Cp3, global, whereis_name, [test]))), - % test re_register - ?line {Pid2, yes} = start_proc(test), - ?line ?UNTIL(Pid2 =:= rpc:call(Cp3, global, whereis_name, [test])), + %% test re_register + {Pid2, yes} = start_proc(test), + ?UNTIL(Pid2 =:= rpc:call(Cp3, global, whereis_name, [test])), Pid3 = rpc:call(Cp3, ?MODULE, start_proc2, [test]), - ?line ?UNTIL(Pid3 =:= rpc:call(Cp3, global, whereis_name, [test])), + ?UNTIL(Pid3 =:= rpc:call(Cp3, global, whereis_name, [test])), Pid3 = global:whereis_name(test), - % test sending + %% test sending global:send(test, {ping, self()}), receive {pong, Cp3} -> ok after - 2000 -> test_server:fail(timeout1) + 2000 -> ct:fail(timeout1) end, rpc:call(Cp1, global, send, [test, {ping, self()}]), receive {pong, Cp3} -> ok after - 2000 -> test_server:fail(timeout2) + 2000 -> ct:fail(timeout2) end, - ?line _ = global:unregister_name(test), - ?line - ?UNTIL((undefined =:= global:whereis_name(test)) and + _ = global:unregister_name(test), + ?UNTIL((undefined =:= global:whereis_name(test)) and (undefined =:= rpc:call(Cp1, global, whereis_name, [test])) and (undefined =:= rpc:call(Cp2, global, whereis_name, [test])) and (undefined =:= rpc:call(Cp3, global, whereis_name, [test]))), exit_p(Pid3), - ?line ?UNTIL(undefined =:= global:whereis_name(test)), + ?UNTIL(undefined =:= global:whereis_name(test)), - % register a proc - ?line {_Pid6, yes} = rpc:call(Cp3, ?MODULE, start_proc, [test]), + %% register a proc + {_Pid6, yes} = rpc:call(Cp3, ?MODULE, start_proc, [test]), write_high_level_trace(Config), - % stop the nodes, and make sure names are released. + + %% stop the nodes, and make sure names are released. stop_node(Cp1), stop_node(Cp2), stop_node(Cp3), - ?line ?UNTIL(undefined =:= global:whereis_name(test)), + ?UNTIL(undefined =:= global:whereis_name(test)), exit_p(Pid2), - ?line ?UNTIL(undefined =:= global:whereis_name(test)), - ?line init_condition(Config), + ?UNTIL(undefined =:= global:whereis_name(test)), + init_condition(Config), ok. -names_hidden(suite) -> []; -names_hidden(doc) -> - ["Tests that names on a hidden node doesn't interfere with names on " - "visible nodes."]; +%% Tests that names on a hidden node doesn't interfere with names on +%% visible nodes. names_hidden(Config) when is_list(Config) -> Timeout = 30, ct:timetrap({seconds,Timeout}), init_high_level_trace(Timeout), - ?line init_condition(Config), - ?line OrigNames = global:registered_names(), - ?line OrigNodes = nodes(), - - ?line {ok, Cp1} = start_node(cp1, Config), - ?line {ok, Cp2} = start_node(cp2, Config), - ?line {ok, Cp3} = start_hidden_node(cp3, Config), - ?line pong = rpc:call(Cp1, net_adm, ping, [Cp3]), - ?line pong = rpc:call(Cp3, net_adm, ping, [Cp2]), - ?line pong = rpc:call(Cp3, net_adm, ping, [node()]), - - ?line [] = [Cp1, Cp2 | OrigNodes] -- nodes(), - - % start a proc on hidden node and register it - ?line {HPid, yes} = rpc:call(Cp3, ?MODULE, start_proc, [test]), - ?line Cp3 = node(HPid), - - % Check that it didn't get registered on visible nodes - ?line - ?UNTIL((undefined =:= global:whereis_name(test)) and + init_condition(Config), + OrigNames = global:registered_names(), + OrigNodes = nodes(), + + {ok, Cp1} = start_node(cp1, Config), + {ok, Cp2} = start_node(cp2, Config), + {ok, Cp3} = start_hidden_node(cp3, Config), + pong = rpc:call(Cp1, net_adm, ping, [Cp3]), + pong = rpc:call(Cp3, net_adm, ping, [Cp2]), + pong = rpc:call(Cp3, net_adm, ping, [node()]), + + [] = [Cp1, Cp2 | OrigNodes] -- nodes(), + + %% start a proc on hidden node and register it + {HPid, yes} = rpc:call(Cp3, ?MODULE, start_proc, [test]), + Cp3 = node(HPid), + + %% Check that it didn't get registered on visible nodes + ?UNTIL((undefined =:= global:whereis_name(test)) and (undefined =:= rpc:call(Cp1, global, whereis_name, [test])) and (undefined =:= rpc:call(Cp2, global, whereis_name, [test]))), - % start a proc on visible node and register it - ?line {Pid, yes} = start_proc(test), - ?line true = (Pid =/= HPid), + %% start a proc on visible node and register it + {Pid, yes} = start_proc(test), + true = (Pid =/= HPid), - % test that it is registered at all nodes - ?line - ?UNTIL((Pid =:= global:whereis_name(test)) and + %% test that it is registered at all nodes + ?UNTIL((Pid =:= global:whereis_name(test)) and (Pid =:= rpc:call(Cp1, global, whereis_name, [test])) and (Pid =:= rpc:call(Cp2, global, whereis_name, [test])) and (HPid =:= rpc:call(Cp3, global, whereis_name, [test])) and ([test] =:= global:registered_names() -- OrigNames)), - % try to register the same name - ?line no = global:register_name(test, self()), - ?line no = rpc:call(Cp1, global, register_name, [test, self()]), + %% try to register the same name + no = global:register_name(test, self()), + no = rpc:call(Cp1, global, register_name, [test, self()]), - % let process exit, check that it is unregistered automatically + %% let process exit, check that it is unregistered automatically exit_p(Pid), - ?line - ?UNTIL((undefined =:= global:whereis_name(test)) and + ?UNTIL((undefined =:= global:whereis_name(test)) and (undefined =:= rpc:call(Cp1, global, whereis_name, [test])) and (undefined =:= rpc:call(Cp2, global, whereis_name, [test])) and (HPid =:= rpc:call(Cp3, global, whereis_name, [test]))), - % test re_register - ?line {Pid2, yes} = start_proc(test), - ?line ?UNTIL(Pid2 =:= rpc:call(Cp2, global, whereis_name, [test])), + %% test re_register + {Pid2, yes} = start_proc(test), + ?UNTIL(Pid2 =:= rpc:call(Cp2, global, whereis_name, [test])), Pid3 = rpc:call(Cp2, ?MODULE, start_proc2, [test]), - ?line ?UNTIL(Pid3 =:= rpc:call(Cp2, global, whereis_name, [test])), - ?line Pid3 = global:whereis_name(test), + ?UNTIL(Pid3 =:= rpc:call(Cp2, global, whereis_name, [test])), + Pid3 = global:whereis_name(test), - % test sending - ?line Pid3 = global:send(test, {ping, self()}), + %% test sending + Pid3 = global:send(test, {ping, self()}), receive {pong, Cp2} -> ok after - 2000 -> test_server:fail(timeout1) + 2000 -> ct:fail(timeout1) end, rpc:call(Cp1, global, send, [test, {ping, self()}]), receive {pong, Cp2} -> ok after - 2000 -> test_server:fail(timeout2) + 2000 -> ct:fail(timeout2) end, - ?line _ = rpc:call(Cp3, global, unregister_name, [test]), - ?line - ?UNTIL((Pid3 =:= global:whereis_name(test)) and + _ = rpc:call(Cp3, global, unregister_name, [test]), + ?UNTIL((Pid3 =:= global:whereis_name(test)) and (Pid3 =:= rpc:call(Cp1, global, whereis_name, [test])) and (Pid3 =:= rpc:call(Cp2, global, whereis_name, [test])) and (undefined =:= rpc:call(Cp3, global, whereis_name, [test]))), - ?line _ = global:unregister_name(test), - ?line - ?UNTIL((undefined =:= global:whereis_name(test)) and + _ = global:unregister_name(test), + ?UNTIL((undefined =:= global:whereis_name(test)) and (undefined =:= rpc:call(Cp1, global, whereis_name, [test])) and (undefined =:= rpc:call(Cp2, global, whereis_name, [test])) and (undefined =:= rpc:call(Cp3, global, whereis_name, [test]))), @@ -607,277 +592,297 @@ names_hidden(Config) when is_list(Config) -> exit_p(Pid3), exit_p(HPid), - ?line ?UNTIL(undefined =:= global:whereis_name(test)), + ?UNTIL(undefined =:= global:whereis_name(test)), write_high_level_trace(Config), - % stop the nodes, and make sure names are released. + + %% stop the nodes, and make sure names are released. stop_node(Cp1), stop_node(Cp2), stop_node(Cp3), - ?line init_condition(Config), + init_condition(Config), ok. -locks(suite) -> []; locks(Config) when is_list(Config) -> Timeout = 30, ct:timetrap({seconds,Timeout}), init_high_level_trace(Timeout), - ?line init_condition(Config), - ?line {ok, Cp1} = start_node(cp1, Config), - ?line {ok, Cp2} = start_node(cp2, Config), - ?line {ok, Cp3} = start_node(cp3, Config), - - ?line wait_for_ready_net(Config), - - % start two procs - ?line Pid = start_proc(), - ?line Pid2 = rpc:call(Cp1, ?MODULE, start_proc, []), - % set a lock, and make sure noone else can set the same lock - ?line true = global:set_lock({test_lock, self()}, ?NODES, 1), - ?line false = req(Pid, {set_lock, test_lock, self()}), - ?line false = req(Pid2, {set_lock, test_lock, self()}), - % delete, and let another proc set the lock + init_condition(Config), + {ok, Cp1} = start_node(cp1, Config), + {ok, Cp2} = start_node(cp2, Config), + {ok, Cp3} = start_node(cp3, Config), + + wait_for_ready_net(Config), + + %% start two procs + Pid = start_proc(), + Pid2 = rpc:call(Cp1, ?MODULE, start_proc, []), + + %% set a lock, and make sure noone else can set the same lock + true = global:set_lock({test_lock, self()}, ?NODES, 1), + false = req(Pid, {set_lock, test_lock, self()}), + false = req(Pid2, {set_lock, test_lock, self()}), + + %% delete, and let another proc set the lock global:del_lock({test_lock, self()}), - ?line true = req(Pid, {set_lock, test_lock, self()}), - ?line false = req(Pid2, {set_lock, test_lock, self()}), - ?line false = global:set_lock({test_lock, self()}, ?NODES,1), - % kill lock-holding proc, make sure the lock is released + true = req(Pid, {set_lock, test_lock, self()}), + false = req(Pid2, {set_lock, test_lock, self()}), + false = global:set_lock({test_lock, self()}, ?NODES,1), + + %% kill lock-holding proc, make sure the lock is released exit_p(Pid), ?UNTIL(true =:= global:set_lock({test_lock, self()}, ?NODES,1)), Pid2 ! {set_lock_loop, test_lock, self()}, - % make sure we don't have the msg + + %% make sure we don't have the msg receive - {got_lock, Pid2} -> test_server:fail(got_lock) + {got_lock, Pid2} -> ct:fail(got_lock) after 1000 -> ok end, global:del_lock({test_lock, self()}), - % make sure pid2 got the lock + + %% make sure pid2 got the lock receive {got_lock, Pid2} -> ok after - % 12000 >> 5000, which is the max time before a new retry for - % set_lock - 12000 -> test_server:fail(got_lock2) + %% 12000 >> 5000, which is the max time before a new retry for + %% set_lock + 12000 -> ct:fail(got_lock2) end, - % let proc set the same lock - ?line true = req(Pid2, {set_lock, test_lock, self()}), - % let proc set new lock - ?line true = req(Pid2, {set_lock, test_lock2, self()}), - ?line false = global:set_lock({test_lock, self()},?NODES,1), - ?line false = global:set_lock({test_lock2, self()}, ?NODES,1), + %% let proc set the same lock + true = req(Pid2, {set_lock, test_lock, self()}), + + %% let proc set new lock + true = req(Pid2, {set_lock, test_lock2, self()}), + false = global:set_lock({test_lock, self()},?NODES,1), + false = global:set_lock({test_lock2, self()}, ?NODES,1), exit_p(Pid2), -% erlang:display({locks1, ets:tab2list(global_locks)}), ?UNTIL(true =:= global:set_lock({test_lock, self()}, ?NODES, 1)), ?UNTIL(true =:= global:set_lock({test_lock2, self()}, ?NODES, 1)), - ?line global:del_lock({test_lock, self()}), - ?line global:del_lock({test_lock2, self()}), - - % let proc set two locks - ?line Pid3 = rpc:call(Cp1, ?MODULE, start_proc, []), - ?line true = req(Pid3, {set_lock, test_lock, self()}), - ?line true = req(Pid3, {set_lock, test_lock2, self()}), - % del one lock - ?line Pid3 ! {del_lock, test_lock2}, - ?line test_server:sleep(100), - % check that one lock is still set, but not the other - ?line false = global:set_lock({test_lock, self()}, ?NODES, 1), - ?line true = global:set_lock({test_lock2, self()}, ?NODES, 1), - ?line global:del_lock({test_lock2, self()}), - % kill lock-holder + global:del_lock({test_lock, self()}), + global:del_lock({test_lock2, self()}), + + %% let proc set two locks + Pid3 = rpc:call(Cp1, ?MODULE, start_proc, []), + true = req(Pid3, {set_lock, test_lock, self()}), + true = req(Pid3, {set_lock, test_lock2, self()}), + + %% del one lock + Pid3 ! {del_lock, test_lock2}, + ct:sleep(100), + + %% check that one lock is still set, but not the other + false = global:set_lock({test_lock, self()}, ?NODES, 1), + true = global:set_lock({test_lock2, self()}, ?NODES, 1), + global:del_lock({test_lock2, self()}), + + %% kill lock-holder exit_p(Pid3), -% erlang:display({locks2, ets:tab2list(global_locks)}), + ?UNTIL(true =:= global:set_lock({test_lock, self()}, ?NODES, 1)), - ?line global:del_lock({test_lock, self()}), + global:del_lock({test_lock, self()}), ?UNTIL(true =:= global:set_lock({test_lock2, self()}, ?NODES, 1)), - ?line global:del_lock({test_lock2, self()}), - - % start one proc on each node - ?line Pid4 = start_proc(), - ?line Pid5 = rpc:call(Cp1, ?MODULE, start_proc, []), - ?line Pid6 = rpc:call(Cp2, ?MODULE, start_proc, []), - ?line Pid7 = rpc:call(Cp3, ?MODULE, start_proc, []), - % set lock on two nodes - ?line true = req(Pid4, {set_lock, test_lock, self(), [node(), Cp1]}), - ?line false = req(Pid5, {set_lock, test_lock, self(), [node(), Cp1]}), - % set same lock on other two nodes - ?line true = req(Pid6, {set_lock, test_lock, self(), [Cp2, Cp3]}), - ?line false = req(Pid7, {set_lock, test_lock, self(), [Cp2, Cp3]}), - % release lock + global:del_lock({test_lock2, self()}), + + %% start one proc on each node + Pid4 = start_proc(), + Pid5 = rpc:call(Cp1, ?MODULE, start_proc, []), + Pid6 = rpc:call(Cp2, ?MODULE, start_proc, []), + Pid7 = rpc:call(Cp3, ?MODULE, start_proc, []), + + %% set lock on two nodes + true = req(Pid4, {set_lock, test_lock, self(), [node(), Cp1]}), + false = req(Pid5, {set_lock, test_lock, self(), [node(), Cp1]}), + + %% set same lock on other two nodes + true = req(Pid6, {set_lock, test_lock, self(), [Cp2, Cp3]}), + false = req(Pid7, {set_lock, test_lock, self(), [Cp2, Cp3]}), + + %% release lock Pid6 ! {del_lock, test_lock, [Cp2, Cp3]}, - % try to set lock on a node that already has the lock - ?line false = req(Pid6, {set_lock, test_lock, self(), [Cp1, Cp2, Cp3]}), - % set lock on a node + %% try to set lock on a node that already has the lock + false = req(Pid6, {set_lock, test_lock, self(), [Cp1, Cp2, Cp3]}), + + %% set lock on a node exit_p(Pid4), ?UNTIL(true =:= req(Pid5, {set_lock, test_lock, self(), [node(), Cp1]})), - ?line Pid8 = start_proc(), - ?line false = req(Pid8, {set_lock, test_lock, self()}), + Pid8 = start_proc(), + false = req(Pid8, {set_lock, test_lock, self()}), write_high_level_trace(Config), - % stop the nodes, and make sure locks are released. + + %% stop the nodes, and make sure locks are released. stop_node(Cp1), stop_node(Cp2), stop_node(Cp3), - ?line test_server:sleep(100), - ?line true = req(Pid8, {set_lock, test_lock, self()}), + ct:sleep(100), + true = req(Pid8, {set_lock, test_lock, self()}), exit_p(Pid8), - ?line test_server:sleep(10), + ct:sleep(10), - ?line init_condition(Config), + init_condition(Config), ok. - -locks_hidden(suite) -> []; -locks_hidden(doc) -> - ["Tests that locks on a hidden node doesn't interere with locks on " - "visible nodes."]; + +%% Tests that locks on a hidden node doesn't interere with locks on +%% visible nodes. locks_hidden(Config) when is_list(Config) -> Timeout = 30, ct:timetrap({seconds,Timeout}), init_high_level_trace(Timeout), - ?line init_condition(Config), - ?line OrigNodes = nodes(), - ?line {ok, Cp1} = start_node(cp1, Config), - ?line {ok, Cp2} = start_node(cp2, Config), - ?line {ok, Cp3} = start_hidden_node(cp3, Config), - ?line pong = rpc:call(Cp1, net_adm, ping, [Cp3]), - ?line pong = rpc:call(Cp3, net_adm, ping, [Cp2]), - ?line pong = rpc:call(Cp3, net_adm, ping, [node()]), - - ?line [] = [Cp1, Cp2 | OrigNodes] -- nodes(), - - % start two procs - ?line Pid = start_proc(), - ?line Pid2 = rpc:call(Cp1, ?MODULE, start_proc, []), - ?line HPid = rpc:call(Cp3, ?MODULE, start_proc, []), - % Make sure hidden node doesn't interfere with visible nodes lock - ?line true = req(HPid, {set_lock, test_lock, self()}), - ?line true = global:set_lock({test_lock, self()}, ?NODES, 1), - ?line false = req(Pid, {set_lock, test_lock, self()}), - ?line true = req(HPid, {del_lock_sync, test_lock, self()}), - ?line false = req(Pid2, {set_lock, test_lock, self()}), - % delete, and let another proc set the lock + init_condition(Config), + OrigNodes = nodes(), + {ok, Cp1} = start_node(cp1, Config), + {ok, Cp2} = start_node(cp2, Config), + {ok, Cp3} = start_hidden_node(cp3, Config), + pong = rpc:call(Cp1, net_adm, ping, [Cp3]), + pong = rpc:call(Cp3, net_adm, ping, [Cp2]), + pong = rpc:call(Cp3, net_adm, ping, [node()]), + + [] = [Cp1, Cp2 | OrigNodes] -- nodes(), + + %% start two procs + Pid = start_proc(), + Pid2 = rpc:call(Cp1, ?MODULE, start_proc, []), + HPid = rpc:call(Cp3, ?MODULE, start_proc, []), + + %% Make sure hidden node doesn't interfere with visible nodes lock + true = req(HPid, {set_lock, test_lock, self()}), + true = global:set_lock({test_lock, self()}, ?NODES, 1), + false = req(Pid, {set_lock, test_lock, self()}), + true = req(HPid, {del_lock_sync, test_lock, self()}), + false = req(Pid2, {set_lock, test_lock, self()}), + + %% delete, and let another proc set the lock global:del_lock({test_lock, self()}), - ?line true = req(Pid, {set_lock, test_lock, self()}), - ?line false = req(Pid2, {set_lock, test_lock, self()}), - ?line false = global:set_lock({test_lock, self()}, ?NODES,1), - % kill lock-holding proc, make sure the lock is released + true = req(Pid, {set_lock, test_lock, self()}), + false = req(Pid2, {set_lock, test_lock, self()}), + false = global:set_lock({test_lock, self()}, ?NODES,1), + + %% kill lock-holding proc, make sure the lock is released exit_p(Pid), ?UNTIL(true =:= global:set_lock({test_lock, self()}, ?NODES, 1)), ?UNTIL(true =:= req(HPid, {set_lock, test_lock, self()})), Pid2 ! {set_lock_loop, test_lock, self()}, - % make sure we don't have the msg + + %% make sure we don't have the msg receive - {got_lock, Pid2} -> test_server:fail(got_lock) + {got_lock, Pid2} -> ct:fail(got_lock) after 1000 -> ok end, global:del_lock({test_lock, self()}), - % make sure pid2 got the lock + + %% make sure pid2 got the lock receive {got_lock, Pid2} -> ok after - % 12000 >> 5000, which is the max time before a new retry for - % set_lock - 12000 -> test_server:fail(got_lock2) + %% 12000 >> 5000, which is the max time before a new retry for + %% set_lock + 12000 -> ct:fail(got_lock2) end, - ?line true = req(HPid, {del_lock_sync, test_lock, self()}), - - % let proc set the same lock - ?line true = req(Pid2, {set_lock, test_lock, self()}), - % let proc set new lock - ?line true = req(Pid2, {set_lock, test_lock2, self()}), - ?line true = req(HPid, {set_lock, test_lock, self()}), - ?line true = req(HPid, {set_lock, test_lock2, self()}), + true = req(HPid, {del_lock_sync, test_lock, self()}), + + %% let proc set the same lock + true = req(Pid2, {set_lock, test_lock, self()}), + + %% let proc set new lock + true = req(Pid2, {set_lock, test_lock2, self()}), + true = req(HPid, {set_lock, test_lock, self()}), + true = req(HPid, {set_lock, test_lock2, self()}), exit_p(HPid), - ?line false = global:set_lock({test_lock, self()},?NODES,1), - ?line false = global:set_lock({test_lock2, self()}, ?NODES,1), + false = global:set_lock({test_lock, self()},?NODES,1), + false = global:set_lock({test_lock2, self()}, ?NODES,1), + exit_p(Pid2), -% erlang:display({locks1, ets:tab2list(global_locks)}), ?UNTIL(true =:= global:set_lock({test_lock, self()}, ?NODES, 1)), ?UNTIL(true =:= global:set_lock({test_lock2, self()}, ?NODES, 1)), - ?line global:del_lock({test_lock, self()}), - ?line global:del_lock({test_lock2, self()}), + global:del_lock({test_lock, self()}), + global:del_lock({test_lock2, self()}), write_high_level_trace(Config), - % stop the nodes, and make sure locks are released. + + %% stop the nodes, and make sure locks are released. stop_node(Cp1), stop_node(Cp2), stop_node(Cp3), - ?line init_condition(Config), + init_condition(Config), ok. - -bad_input(suite) -> []; + bad_input(Config) when is_list(Config) -> Timeout = 15, ct:timetrap({seconds,Timeout}), init_high_level_trace(Timeout), - ?line init_condition(Config), + init_condition(Config), + Pid = whereis(global_name_server), + {'EXIT', _} = (catch global:set_lock(bad_id)), + {'EXIT', _} = (catch global:set_lock({id, self()}, bad_nodes)), + {'EXIT', _} = (catch global:del_lock(bad_id)), + {'EXIT', _} = (catch global:del_lock({id, self()}, bad_nodes)), + {'EXIT', _} = (catch global:register_name(name, bad_pid)), + {'EXIT', _} = (catch global:reregister_name(name, bad_pid)), + {'EXIT', _} = (catch global:trans(bad_id, {m,f})), + {'EXIT', _} = (catch global:trans({id, self()}, {m,f}, [node()], -1)), Pid = whereis(global_name_server), - ?line {'EXIT', _} = (catch global:set_lock(bad_id)), - ?line {'EXIT', _} = (catch global:set_lock({id, self()}, bad_nodes)), - ?line {'EXIT', _} = (catch global:del_lock(bad_id)), - ?line {'EXIT', _} = (catch global:del_lock({id, self()}, bad_nodes)), - ?line {'EXIT', _} = (catch global:register_name(name, bad_pid)), - ?line {'EXIT', _} = (catch global:reregister_name(name, bad_pid)), - ?line {'EXIT', _} = (catch global:trans(bad_id, {m,f})), - ?line {'EXIT', _} = (catch global:trans({id, self()}, {m,f}, [node()], -1)), - ?line Pid = whereis(global_name_server), - ?line init_condition(Config), + init_condition(Config), ok. -names_and_locks(suite) -> []; names_and_locks(Config) when is_list(Config) -> Timeout = 30, ct:timetrap({seconds,Timeout}), init_high_level_trace(Timeout), - ?line init_condition(Config), - ?line OrigNames = global:registered_names(), - - ?line {ok, Cp1} = start_node(cp1, Config), - ?line {ok, Cp2} = start_node(cp2, Config), - ?line {ok, Cp3} = start_node(cp3, Config), - - % start one proc on each node - ?line PidTS = start_proc(), - ?line Pid1 = rpc:call(Cp1, ?MODULE, start_proc, []), - ?line Pid2 = rpc:call(Cp2, ?MODULE, start_proc, []), - ?line Pid3 = rpc:call(Cp3, ?MODULE, start_proc, []), - % register some of them - ?line yes = global:register_name(test1, Pid1), - ?line yes = global:register_name(test2, Pid2), - ?line yes = global:register_name(test3, Pid3), - ?line no = global:register_name(test3, PidTS), - ?line yes = global:register_name(test4, PidTS), - - % set lock on two nodes - ?line true = req(PidTS, {set_lock, test_lock, self(), [node(), Cp1]}), - ?line false = req(Pid1, {set_lock, test_lock, self(), [node(), Cp1]}), - % set same lock on other two nodes - ?line true = req(Pid2, {set_lock, test_lock, self(), [Cp2, Cp3]}), - ?line false = req(Pid3, {set_lock, test_lock, self(), [Cp2, Cp3]}), - % release lock + init_condition(Config), + OrigNames = global:registered_names(), + + {ok, Cp1} = start_node(cp1, Config), + {ok, Cp2} = start_node(cp2, Config), + {ok, Cp3} = start_node(cp3, Config), + + %% start one proc on each node + PidTS = start_proc(), + Pid1 = rpc:call(Cp1, ?MODULE, start_proc, []), + Pid2 = rpc:call(Cp2, ?MODULE, start_proc, []), + Pid3 = rpc:call(Cp3, ?MODULE, start_proc, []), + + %% register some of them + yes = global:register_name(test1, Pid1), + yes = global:register_name(test2, Pid2), + yes = global:register_name(test3, Pid3), + no = global:register_name(test3, PidTS), + yes = global:register_name(test4, PidTS), + + %% set lock on two nodes + true = req(PidTS, {set_lock, test_lock, self(), [node(), Cp1]}), + false = req(Pid1, {set_lock, test_lock, self(), [node(), Cp1]}), + + %% set same lock on other two nodes + true = req(Pid2, {set_lock, test_lock, self(), [Cp2, Cp3]}), + false = req(Pid3, {set_lock, test_lock, self(), [Cp2, Cp3]}), + + %% release lock Pid2 ! {del_lock, test_lock, [Cp2, Cp3]}, - ?line test_server:sleep(100), - % try to set lock on a node that already has the lock - ?line false = req(Pid2, {set_lock, test_lock, self(), [Cp1, Cp2, Cp3]}), - % set two locks - ?line true = req(Pid2, {set_lock, test_lock, self(), [Cp2, Cp3]}), - ?line true = req(Pid2, {set_lock, test_lock2, self(), [Cp2, Cp3]}), - - % kill some processes, make sure all locks/names are released + ct:sleep(100), + + %% try to set lock on a node that already has the lock + false = req(Pid2, {set_lock, test_lock, self(), [Cp1, Cp2, Cp3]}), + + %% set two locks + true = req(Pid2, {set_lock, test_lock, self(), [Cp2, Cp3]}), + true = req(Pid2, {set_lock, test_lock2, self(), [Cp2, Cp3]}), + + %% kill some processes, make sure all locks/names are released exit_p(PidTS), - ?line ?UNTIL(undefined =:= global:whereis_name(test4)), - ?line true = global:set_lock({test_lock, self()}, [node(), Cp1], 1), + ?UNTIL(undefined =:= global:whereis_name(test4)), + true = global:set_lock({test_lock, self()}, [node(), Cp1], 1), global:del_lock({test_lock, self()}, [node(), Cp1]), exit_p(Pid2), - ?line - ?UNTIL((undefined =:= global:whereis_name(test2)) and + ?UNTIL((undefined =:= global:whereis_name(test2)) and (true =:= global:set_lock({test_lock, self()}, [Cp2, Cp3], 1)) and (true =:= global:set_lock({test_lock2, self()}, [Cp2, Cp3], 1))), @@ -887,117 +892,113 @@ names_and_locks(Config) when is_list(Config) -> exit_p(Pid1), exit_p(Pid3), - ?line ?UNTIL(OrigNames =:= global:registered_names()), + ?UNTIL(OrigNames =:= global:registered_names()), write_high_level_trace(Config), stop_node(Cp1), stop_node(Cp2), stop_node(Cp3), - ?line init_condition(Config), + init_condition(Config), ok. - -lock_die(suite) -> []; -lock_die(doc) -> - ["OTP-6341. Remove locks using monitors."]; + +%% OTP-6341. Remove locks using monitors. lock_die(Config) when is_list(Config) -> Timeout = 30, ct:timetrap({seconds,Timeout}), init_high_level_trace(Timeout), - ?line init_condition(Config), - ?line OrigNames = global:registered_names(), + init_condition(Config), + OrigNames = global:registered_names(), - ?line {ok, Cp1} = start_node(cp1, Config), - ?line {ok, Cp2} = start_node(cp2, Config), + {ok, Cp1} = start_node(cp1, Config), + {ok, Cp2} = start_node(cp2, Config), %% First test. LockId = {id, self()}, - ?line Pid2 = start_proc(), - ?line true = req(Pid2, {set_lock2, LockId, self()}), + Pid2 = start_proc(), + true = req(Pid2, {set_lock2, LockId, self()}), - ?line true = global:set_lock(LockId, [Cp1]), + true = global:set_lock(LockId, [Cp1]), %% Id is locked on Cp1 and Cp2 (by Pid2) but not by self(): %% (there is no mon. ref) - ?line _ = global:del_lock(LockId, [node(), Cp1, Cp2]), + _ = global:del_lock(LockId, [node(), Cp1, Cp2]), + + exit_p(Pid2), - ?line exit_p(Pid2), - %% Second test. - ?line Pid3 = start_proc(), - ?line true = req(Pid3, {set_lock, id, self(), [Cp1]}), + Pid3 = start_proc(), + true = req(Pid3, {set_lock, id, self(), [Cp1]}), %% The lock is removed from Cp1 thanks to monitors. - ?line exit_p(Pid3), - - ?line true = global:set_lock(LockId, [node(), Cp1]), - ?line _ = global:del_lock(LockId, [node(), Cp1]), + exit_p(Pid3), - ?line ?UNTIL(OrigNames =:= global:registered_names()), + true = global:set_lock(LockId, [node(), Cp1]), + _ = global:del_lock(LockId, [node(), Cp1]), + + ?UNTIL(OrigNames =:= global:registered_names()), write_high_level_trace(Config), stop_node(Cp1), stop_node(Cp2), - ?line init_condition(Config), + init_condition(Config), ok. -name_die(suite) -> []; -name_die(doc) -> - ["OTP-6341. Remove names using monitors."]; +%% OTP-6341. Remove names using monitors. name_die(Config) when is_list(Config) -> Timeout = 30, ct:timetrap({seconds,Timeout}), init_high_level_trace(Timeout), - ?line init_condition(Config), - ?line OrigNames = global:registered_names(), - ?line [Cp1] = Cps = start_nodes([z], peer, Config), % z > test_server + init_condition(Config), + OrigNames = global:registered_names(), + [Cp1] = Cps = start_nodes([z], peer, Config), % z > test_server Nodes = lists:sort([node() | Cps]), - ?line wait_for_ready_net(Config), - + wait_for_ready_net(Config), + Name = name_die, - ?line Pid = rpc:call(Cp1, ?MODULE, start_proc, []), + Pid = rpc:call(Cp1, ?MODULE, start_proc, []), %% Test 1. No resolver is called if the same pid is registered on %% both partitions. T1 = node(), Part1 = [T1], Part2 = [Cp1], - ?line rpc_cast(Cp1, - ?MODULE, part_2_2, [Config, - Part1, - Part2, - []]), - ?line ?UNTIL(is_ready_partition(Config)), - ?line ?UNTIL(undefined =:= global:whereis_name(Name)), - ?line yes = global:register_name(Name, Pid), - - ?line pong = net_adm:ping(Cp1), - ?line wait_for_ready_net(Nodes, Config), - ?line assert_pid(global:whereis_name(Name)), + rpc_cast(Cp1, + ?MODULE, part_2_2, [Config, + Part1, + Part2, + []]), + ?UNTIL(is_ready_partition(Config)), + ?UNTIL(undefined =:= global:whereis_name(Name)), + yes = global:register_name(Name, Pid), + + pong = net_adm:ping(Cp1), + wait_for_ready_net(Nodes, Config), + assert_pid(global:whereis_name(Name)), exit_p(Pid), - ?line ?UNTIL(OrigNames =:= global:registered_names()), + ?UNTIL(OrigNames =:= global:registered_names()), %% Test 2. Register a name running outside the current partition. %% Killing the pid will not remove the name from the current %% partition, unless monitors are used. - ?line Pid2 = rpc:call(Cp1, ?MODULE, start_proc, []), - Dir = ?config(priv_dir, Config), + Pid2 = rpc:call(Cp1, ?MODULE, start_proc, []), + Dir = proplists:get_value(priv_dir, Config), KillFile = filename:join([Dir, "kill.txt"]), file:delete(KillFile), - ?line erlang:spawn(Cp1, fun() -> kill_pid(Pid2, KillFile, Config) end), - ?line rpc_cast(Cp1, - ?MODULE, part_2_2, [Config, - Part1, - Part2, - []]), - ?line ?UNTIL(is_ready_partition(Config)), - ?line ?UNTIL(undefined =:= global:whereis_name(Name)), - ?line yes = global:register_name(Name, Pid2), - ?line touch(KillFile, "kill"), - ?line file_contents(KillFile, "done", Config), + erlang:spawn(Cp1, fun() -> kill_pid(Pid2, KillFile, Config) end), + rpc_cast(Cp1, + ?MODULE, part_2_2, [Config, + Part1, + Part2, + []]), + ?UNTIL(is_ready_partition(Config)), + ?UNTIL(undefined =:= global:whereis_name(Name)), + yes = global:register_name(Name, Pid2), + touch(KillFile, "kill"), + file_contents(KillFile, "done", Config), file:delete(KillFile), - ?line ?UNTIL(OrigNames =:= global:registered_names()), + ?UNTIL(OrigNames =:= global:registered_names()), write_high_level_trace(Config), stop_nodes(Cps), - ?line init_condition(Config), + init_condition(Config), ok. kill_pid(Pid, File, Config) -> @@ -1005,186 +1006,178 @@ kill_pid(Pid, File, Config) -> exit_p(Pid), touch(File, "done"). -basic_partition(suite) -> []; -basic_partition(doc) -> - ["Tests that two partitioned networks exchange correct info."]; +%% Tests that two partitioned networks exchange correct info. basic_partition(Config) when is_list(Config) -> Timeout = 30, ct:timetrap({seconds,Timeout}), init_high_level_trace(Timeout), - ?line init_condition(Config), - ?line OrigNames = global:registered_names(), - - ?line [Cp1, Cp2, Cp3] = start_nodes([cp1, cp2, cp3], peer, Config), - ?line [Cp1, Cp2, Cp3] = lists:sort(nodes()), - - ?line wait_for_ready_net(Config), - - % make cp2 and cp3 connected, partitioned from us and cp1 - ?line rpc_cast(Cp2, ?MODULE, part1, [Config, node(), Cp1, Cp3]), - ?line ?UNTIL(is_ready_partition(Config)), - - % start different processes in both partitions - ?line {Pid, yes} = start_proc(test), - - % connect to other partition - ?line pong = net_adm:ping(Cp2), - ?line pong = net_adm:ping(Cp3), - ?line [Cp1, Cp2, Cp3] = lists:sort(nodes()), - - % check names - ?line ?UNTIL(Pid =:= rpc:call(Cp2, global, whereis_name, [test])), - ?line ?UNTIL(undefined =/= global:whereis_name(test2)), - ?line Pid2 = global:whereis_name(test2), - ?line Pid2 = rpc:call(Cp2, global, whereis_name, [test2]), - ?line assert_pid(Pid2), - ?line Pid3 = global:whereis_name(test4), - ?line ?UNTIL(Pid3 =:= rpc:call(Cp1, global, whereis_name, [test4])), - ?line assert_pid(Pid3), - - % kill all procs - ?line Pid3 = global:send(test4, die), - % sleep to let the proc die + init_condition(Config), + OrigNames = global:registered_names(), + + [Cp1, Cp2, Cp3] = start_nodes([cp1, cp2, cp3], peer, Config), + [Cp1, Cp2, Cp3] = lists:sort(nodes()), + + wait_for_ready_net(Config), + + %% make cp2 and cp3 connected, partitioned from us and cp1 + rpc_cast(Cp2, ?MODULE, part1, [Config, node(), Cp1, Cp3]), + ?UNTIL(is_ready_partition(Config)), + + %% start different processes in both partitions + {Pid, yes} = start_proc(test), + + %% connect to other partition + pong = net_adm:ping(Cp2), + pong = net_adm:ping(Cp3), + [Cp1, Cp2, Cp3] = lists:sort(nodes()), + + %% check names + ?UNTIL(Pid =:= rpc:call(Cp2, global, whereis_name, [test])), + ?UNTIL(undefined =/= global:whereis_name(test2)), + Pid2 = global:whereis_name(test2), + Pid2 = rpc:call(Cp2, global, whereis_name, [test2]), + assert_pid(Pid2), + Pid3 = global:whereis_name(test4), + ?UNTIL(Pid3 =:= rpc:call(Cp1, global, whereis_name, [test4])), + assert_pid(Pid3), + + %% kill all procs + Pid3 = global:send(test4, die), + %% sleep to let the proc die wait_for_exit(Pid3), - ?line ?UNTIL(undefined =:= global:whereis_name(test4)), - + ?UNTIL(undefined =:= global:whereis_name(test4)), + exit_p(Pid), exit_p(Pid2), - ?line ?UNTIL(OrigNames =:= global:registered_names()), + ?UNTIL(OrigNames =:= global:registered_names()), write_high_level_trace(Config), stop_node(Cp1), stop_node(Cp2), stop_node(Cp3), - ?line init_condition(Config), + init_condition(Config), ok. -basic_name_partition(suite) -> - []; -basic_name_partition(doc) -> - ["Creates two partitions with two nodes in each partition.", - "Tests that names are exchanged correctly, and that EXITs", - "during connect phase are handled correctly."]; +%% Creates two partitions with two nodes in each partition. +%% Tests that names are exchanged correctly, and that EXITs +%% during connect phase are handled correctly. basic_name_partition(Config) when is_list(Config) -> Timeout = 60, ct:timetrap({seconds,Timeout}), init_high_level_trace(Timeout), - ?line init_condition(Config), - ?line OrigNames = global:registered_names(), + init_condition(Config), + OrigNames = global:registered_names(), - ?line [Cp1, Cp2, Cp3] = start_nodes([cp1, cp2, cp3], peer, Config), - ?line [Cp1, Cp2, Cp3] = lists:sort(nodes()), + [Cp1, Cp2, Cp3] = start_nodes([cp1, cp2, cp3], peer, Config), + [Cp1, Cp2, Cp3] = lists:sort(nodes()), Nodes = ?NODES, - ?line wait_for_ready_net(Config), + wait_for_ready_net(Config), - % There used to be more than one name registered for some - % processes. That was a mistake; there is no support for more than - % one name per process, and the manual is quite clear about that - % ("equivalent to the register/2 and whereis/1 BIFs"). The - % resolver procedure did not take care of such "duplicated" names, - % which caused this testcase to fail every now and then. + %% There used to be more than one name registered for some + %% processes. That was a mistake; there is no support for more than + %% one name per process, and the manual is quite clear about that + %% ("equivalent to the register/2 and whereis/1 BIFs"). The + %% resolver procedure did not take care of such "duplicated" names, + %% which caused this testcase to fail every now and then. - % make cp2 and cp3 connected, partitioned from us and cp1 - % us: register name03 - % cp1: register name12 - % cp2: register name12 - % cp3: register name03 - - ?line rpc_cast(Cp2, ?MODULE, part1_5, [Config, node(), Cp1, Cp3]), - ?line ?UNTIL(is_ready_partition(Config)), - - % start different processes in both partitions - ?line {_, yes} = start_proc_basic(name03), - ?line {_, yes} = rpc:call(Cp1, ?MODULE, start_proc_basic, [name12]), - test_server:sleep(1000), + %% make cp2 and cp3 connected, partitioned from us and cp1 + %% us: register name03 + %% cp1: register name12 + %% cp2: register name12 + %% cp3: register name03 - % connect to other partition - ?line pong = net_adm:ping(Cp3), - - ?line ?UNTIL([Cp1, Cp2, Cp3] =:= lists:sort(nodes())), - ?line wait_for_ready_net(Config), - % check names - ?line Pid03 = global:whereis_name(name03), - ?line assert_pid(Pid03), - ?line true = lists:member(node(Pid03), [node(), Cp3]), - ?line check_everywhere(Nodes, name03, Config), - - ?line Pid12 = global:whereis_name(name12), - ?line assert_pid(Pid12), - ?line true = lists:member(node(Pid12), [Cp1, Cp2]), - ?line check_everywhere(Nodes, name12, Config), - - % kill all procs - ?line Pid12 = global:send(name12, die), - ?line Pid03 = global:send(name03, die), - % sleep to let the procs die + rpc_cast(Cp2, ?MODULE, part1_5, [Config, node(), Cp1, Cp3]), + ?UNTIL(is_ready_partition(Config)), + + %% start different processes in both partitions + {_, yes} = start_proc_basic(name03), + {_, yes} = rpc:call(Cp1, ?MODULE, start_proc_basic, [name12]), + ct:sleep(1000), + + %% connect to other partition + pong = net_adm:ping(Cp3), + + ?UNTIL([Cp1, Cp2, Cp3] =:= lists:sort(nodes())), + wait_for_ready_net(Config), + + %% check names + Pid03 = global:whereis_name(name03), + assert_pid(Pid03), + true = lists:member(node(Pid03), [node(), Cp3]), + check_everywhere(Nodes, name03, Config), + + Pid12 = global:whereis_name(name12), + assert_pid(Pid12), + true = lists:member(node(Pid12), [Cp1, Cp2]), + check_everywhere(Nodes, name12, Config), + + %% kill all procs + Pid12 = global:send(name12, die), + Pid03 = global:send(name03, die), + + %% sleep to let the procs die wait_for_exit(Pid12), wait_for_exit(Pid03), - ?line ?UNTIL(begin Names = [name03, name12], lists:duplicate(length(Names), undefined) =:= [global:whereis_name(Name) || Name <- Names] end), - - ?line ?UNTIL(OrigNames =:= global:registered_names()), + + ?UNTIL(OrigNames =:= global:registered_names()), write_high_level_trace(Config), stop_node(Cp1), stop_node(Cp2), stop_node(Cp3), - ?line init_condition(Config), + init_condition(Config), ok. -%Peer nodes cp0 - cp6 are started. Break apart the connections from -%cp3-cp6 to cp0-cp2 and test_server so we get two partitions. -%In the cp3-cp6 partition, start one process on each node and register -%using both erlang:register, and global:register (test1 on cp3, test2 on -%cp4, test3 on cp5, test4 on cp6), using different resolution functions: -%default for test1, notify_all_name for test2, random_notify_name for test3 -%and one for test4 that sends a message to test_server and keeps the -%process which is greater in the standard ordering. In the other partition, -%do the same (test1 on test_server, test2 on cp0, test3 on cp1, test4 on cp2). -%Sleep a little, then from test_server, connect to cp3-cp6 in order. -%Check that the values for the registered names are the expected ones, and -%that the messages from test4 arrive. - -advanced_partition(suite) -> - []; -advanced_partition(doc) -> - ["Test that names are resolved correctly when two", - "partitioned networks connect."]; +%% Peer nodes cp0 - cp6 are started. Break apart the connections from +%% cp3-cp6 to cp0-cp2 and test_server so we get two partitions. +%% In the cp3-cp6 partition, start one process on each node and register +%% using both erlang:register, and global:register (test1 on cp3, test2 on +%% cp4, test3 on cp5, test4 on cp6), using different resolution functions: +%% default for test1, notify_all_name for test2, random_notify_name for test3 +%% and one for test4 that sends a message to test_server and keeps the +%% process which is greater in the standard ordering. In the other partition, +%% do the same (test1 on test_server, test2 on cp0, test3 on cp1, test4 on cp2). +%% Sleep a little, then from test_server, connect to cp3-cp6 in order. +%% Check that the values for the registered names are the expected ones, and +%% that the messages from test4 arrive. + +%% Test that names are resolved correctly when two +%% partitioned networks connect. advanced_partition(Config) when is_list(Config) -> Timeout = 60, ct:timetrap({seconds,Timeout}), init_high_level_trace(Timeout), - ?line init_condition(Config), - ?line OrigNames = global:registered_names(), + init_condition(Config), + OrigNames = global:registered_names(), - ?line [Cp0, Cp1, Cp2, Cp3, Cp4, Cp5, Cp6] + [Cp0, Cp1, Cp2, Cp3, Cp4, Cp5, Cp6] = start_nodes([cp0, cp1, cp2, cp3, cp4, cp5, cp6], peer, Config), Nodes = lists:sort([node(), Cp0, Cp1, Cp2, Cp3, Cp4, Cp5, Cp6]), - ?line wait_for_ready_net(Config), + wait_for_ready_net(Config), - % make cp3-cp6 connected, partitioned from us and cp0-cp2 - ?line rpc_cast(Cp3, ?MODULE, part2, - [Config, self(), node(), Cp0, Cp1, Cp2, Cp3, Cp4, Cp5,Cp6]), - ?line ?UNTIL(is_ready_partition(Config)), - - % start different processes in this partition - ?line start_procs(self(), Cp0, Cp1, Cp2, Config), - - % connect to other partition - ?line pong = net_adm:ping(Cp3), - ?line pong = net_adm:ping(Cp4), - ?line pong = net_adm:ping(Cp5), - ?line pong = net_adm:ping(Cp6), - - ?line wait_for_ready_net(Config), + %% make cp3-cp6 connected, partitioned from us and cp0-cp2 + rpc_cast(Cp3, ?MODULE, part2, + [Config, self(), node(), Cp0, Cp1, Cp2, Cp3, Cp4, Cp5,Cp6]), + ?UNTIL(is_ready_partition(Config)), + + %% start different processes in this partition + start_procs(self(), Cp0, Cp1, Cp2, Config), + + %% connect to other partition + pong = net_adm:ping(Cp3), + pong = net_adm:ping(Cp4), + pong = net_adm:ping(Cp5), + pong = net_adm:ping(Cp6), + + wait_for_ready_net(Config), - ?line ?UNTIL(lists:member(undefined, [rpc:call(Cp3, erlang, whereis, [test1]), rpc:call(node(), erlang, whereis, [test1])])), @@ -1199,43 +1192,42 @@ advanced_partition(Config) when is_list(Config) -> Mt3 = rpc:call(Cp1, erlang, whereis, [test3]), _Mt4 = rpc:call(Cp2, erlang, whereis, [test4]), - % check names - ?line Pid1 = global:whereis_name(test1), - ?line Pid1 = rpc:call(Cp3, global, whereis_name, [test1]), - ?line assert_pid(Pid1), - ?line true = lists:member(Pid1, [Nt1, Mt1]), - ?line true = lists:member(undefined, [Nt1, Mt1]), - ?line check_everywhere(Nodes, test1, Config), - - ?line undefined = global:whereis_name(test2), - ?line undefined = rpc:call(Cp3, global, whereis_name, [test2]), - ?line yes = sreq(Nt2, {got_notify, self()}), - ?line yes = sreq(Mt2, {got_notify, self()}), - ?line check_everywhere(Nodes, test2, Config), - - ?line Pid3 = global:whereis_name(test3), - ?line Pid3 = rpc:call(Cp3, global, whereis_name, [test3]), - ?line assert_pid(Pid3), - ?line true = lists:member(Pid3, [Nt3, Mt3]), - ?line no = sreq(Pid3, {got_notify, self()}), - ?line yes = sreq(other(Pid3, [Nt2, Nt3]), {got_notify, self()}), - ?line check_everywhere(Nodes, test3, Config), - - ?line Pid4 = global:whereis_name(test4), - ?line Pid4 = rpc:call(Cp3, global, whereis_name, [test4]), - ?line assert_pid(Pid4), -% ?line true = lists:member(Pid4, [Nt4, Mt4]), - ?line Pid4 = Nt4, - ?line check_everywhere(Nodes, test4, Config), - - ?line 1 = collect_resolves(), - - ?line Pid1 = global:send(test1, die), + %% check names + Pid1 = global:whereis_name(test1), + Pid1 = rpc:call(Cp3, global, whereis_name, [test1]), + assert_pid(Pid1), + true = lists:member(Pid1, [Nt1, Mt1]), + true = lists:member(undefined, [Nt1, Mt1]), + check_everywhere(Nodes, test1, Config), + + undefined = global:whereis_name(test2), + undefined = rpc:call(Cp3, global, whereis_name, [test2]), + yes = sreq(Nt2, {got_notify, self()}), + yes = sreq(Mt2, {got_notify, self()}), + check_everywhere(Nodes, test2, Config), + + Pid3 = global:whereis_name(test3), + Pid3 = rpc:call(Cp3, global, whereis_name, [test3]), + assert_pid(Pid3), + true = lists:member(Pid3, [Nt3, Mt3]), + no = sreq(Pid3, {got_notify, self()}), + yes = sreq(other(Pid3, [Nt2, Nt3]), {got_notify, self()}), + check_everywhere(Nodes, test3, Config), + + Pid4 = global:whereis_name(test4), + Pid4 = rpc:call(Cp3, global, whereis_name, [test4]), + assert_pid(Pid4), + Pid4 = Nt4, + check_everywhere(Nodes, test4, Config), + + 1 = collect_resolves(), + + Pid1 = global:send(test1, die), exit_p(Pid3), exit_p(Pid4), wait_for_exit(Pid1), wait_for_exit(Pid3), - ?line ?UNTIL(OrigNames =:= global:registered_names()), + ?UNTIL(OrigNames =:= global:registered_names()), write_high_level_trace(Config), stop_node(Cp0), @@ -1245,93 +1237,90 @@ advanced_partition(Config) when is_list(Config) -> stop_node(Cp4), stop_node(Cp5), stop_node(Cp6), - ?line init_condition(Config), + init_condition(Config), ok. - -%Peer nodes cp0 - cp6 are started, and partitioned just like in -%advanced_partition. Start cp8, only connected to test_server. Let cp6 -%break apart from the rest, and 12 s later, ping cp0 and cp3, and -%register the name test5. After the same 12 s, let cp5 halt. -%Wait for the death of cp5. Ping cp3 (at the same time as cp6 does). -%Take down cp2. Start cp7, restart cp2. Ping cp4, cp6 and cp8. -%Now, expect all nodes to be connected and have the same picture of all -%registered names. - -stress_partition(suite) -> - []; -stress_partition(doc) -> - ["Stress global, make a partitioned net, make some nodes", - "go up/down a bit."]; + +%% Peer nodes cp0 - cp6 are started, and partitioned just like in +%% advanced_partition. Start cp8, only connected to test_server. Let cp6 +%% break apart from the rest, and 12 s later, ping cp0 and cp3, and +%% register the name test5. After the same 12 s, let cp5 halt. +%% Wait for the death of cp5. Ping cp3 (at the same time as cp6 does). +%% Take down cp2. Start cp7, restart cp2. Ping cp4, cp6 and cp8. +%% Now, expect all nodes to be connected and have the same picture of all +%% registered names. + +%% Stress global, make a partitioned net, make some nodes +%% go up/down a bit. stress_partition(Config) when is_list(Config) -> Timeout = 90, ct:timetrap({seconds,Timeout}), init_high_level_trace(Timeout), - ?line init_condition(Config), - ?line OrigNames = global:registered_names(), + init_condition(Config), + OrigNames = global:registered_names(), - ?line [Cp0, Cp1, Cp2, Cp3, Cp4, Cp5, Cp6] + [Cp0, Cp1, Cp2, Cp3, Cp4, Cp5, Cp6] = start_nodes([cp0, cp1, cp2, cp3, cp4, cp5, cp6], peer, Config), - ?line wait_for_ready_net(Config), + wait_for_ready_net(Config), - % make cp3-cp5 connected, partitioned from us and cp0-cp2 - % cp6 is alone (single node). cp6 pings cp0 and cp3 in 12 secs... - ?line rpc_cast(Cp3, ?MODULE, part3, - [Config, self(), node(), Cp0, Cp1, Cp2, Cp3, Cp4, Cp5,Cp6]), - ?line ?UNTIL(is_ready_partition(Config)), - - % start different processes in this partition - ?line start_procs(self(), Cp0, Cp1, Cp2, Config), + %% make cp3-cp5 connected, partitioned from us and cp0-cp2 + %% cp6 is alone (single node). cp6 pings cp0 and cp3 in 12 secs... + rpc_cast(Cp3, ?MODULE, part3, + [Config, self(), node(), Cp0, Cp1, Cp2, Cp3, Cp4, Cp5,Cp6]), + ?UNTIL(is_ready_partition(Config)), + + %% start different processes in this partition + start_procs(self(), Cp0, Cp1, Cp2, Config), + + {ok, Cp8} = start_peer_node(cp8, Config), - ?line {ok, Cp8} = start_peer_node(cp8, Config), - monitor_node(Cp5, true), receive {nodedown, Cp5} -> ok after - 20000 -> test_server:fail({no_nodedown, Cp5}) + 20000 -> ct:fail({no_nodedown, Cp5}) end, monitor_node(Cp5, false), - % Ok, now cp6 pings us, and cp5 will go down. - - % connect to other partition - ?line pong = net_adm:ping(Cp3), - ?line rpc_cast(Cp2, ?MODULE, crash, [0]), - - % Start new nodes - ?line {ok, Cp7} = start_peer_node(cp7, Config), - ?line {ok, Cp2_2} = start_peer_node(cp2, Config), + %% Ok, now cp6 pings us, and cp5 will go down. + + %% connect to other partition + pong = net_adm:ping(Cp3), + rpc_cast(Cp2, ?MODULE, crash, [0]), + + %% Start new nodes + {ok, Cp7} = start_peer_node(cp7, Config), + {ok, Cp2_2} = start_peer_node(cp2, Config), Nodes = lists:sort([node(), Cp0, Cp1, Cp2_2, Cp3, Cp4, Cp6, Cp7, Cp8]), put(?nodes_tag, Nodes), - ?line pong = net_adm:ping(Cp4), - ?line pong = net_adm:ping(Cp6), - ?line pong = net_adm:ping(Cp8), + pong = net_adm:ping(Cp4), + pong = net_adm:ping(Cp6), + pong = net_adm:ping(Cp8), - ?line wait_for_ready_net(Nodes, Config), + wait_for_ready_net(Nodes, Config), - % Make sure that all nodes have the same picture of all names - ?line check_everywhere(Nodes, test1, Config), - ?line assert_pid(global:whereis_name(test1)), - - ?line check_everywhere(Nodes, test2, Config), - ?line undefined = global:whereis_name(test2), + %% Make sure that all nodes have the same picture of all names + check_everywhere(Nodes, test1, Config), + assert_pid(global:whereis_name(test1)), - ?line check_everywhere(Nodes, test3, Config), - ?line assert_pid(global:whereis_name(test3)), + check_everywhere(Nodes, test2, Config), + undefined = global:whereis_name(test2), - ?line check_everywhere(Nodes, test4, Config), - ?line assert_pid(global:whereis_name(test4)), + check_everywhere(Nodes, test3, Config), + assert_pid(global:whereis_name(test3)), - ?line check_everywhere(Nodes, test5, Config), - ?line ?UNTIL(undefined =:= global:whereis_name(test5)), - - ?line assert_pid(global:send(test1, die)), - ?line assert_pid(global:send(test3, die)), - ?line assert_pid(global:send(test4, die)), + check_everywhere(Nodes, test4, Config), + assert_pid(global:whereis_name(test4)), - ?line ?UNTIL(OrigNames =:= global:registered_names()), + check_everywhere(Nodes, test5, Config), + ?UNTIL(undefined =:= global:whereis_name(test5)), + + assert_pid(global:send(test1, die)), + assert_pid(global:send(test3, die)), + assert_pid(global:send(test4, die)), + + ?UNTIL(OrigNames =:= global:registered_names()), write_high_level_trace(Config), stop_node(Cp0), @@ -1343,15 +1332,13 @@ stress_partition(Config) when is_list(Config) -> stop_node(Cp6), stop_node(Cp7), stop_node(Cp8), - ?line init_condition(Config), + init_condition(Config), ok. %% Use this one to test alot of connection tests -%% erl -sname ts -rsh ctrsh -pa /clearcase/otp/internal_tools/test_server/ebin/ -ring_line 10000 -s test_server run_test global_SUITE +%% erl -sname ts -ring_line 10000 -s test_server run_test global_SUITE -ring_line(suite) -> []; -ring_line(doc) -> [""]; ring_line(Config) when is_list(Config) -> {ok, [[N]]} = init:get_argument(ring_line), loop_it(list_to_integer(N), Config). @@ -1360,74 +1347,70 @@ loop_it(N, Config) -> loop_it(N,N, Config). loop_it(0,_, _Config) -> ok; loop_it(N,M, Config) -> - test_server:format(1, "Round: ~w", [M-N]), + ct:pal(?HI_VERBOSITY, "Round: ~w", [M-N]), ring(Config), line(Config), loop_it(N-1,M, Config). -ring(suite) -> - []; -ring(doc) -> - ["Make 10 single nodes, all having the same name.", - "Make all ping its predecessor, pinging in a ring.", - "Make sure that there's just one winner."]; +%% Make 10 single nodes, all having the same name. +%% Make all ping its predecessor, pinging in a ring. +%% Make sure that there's just one winner. ring(Config) when is_list(Config) -> Timeout = 60, ct:timetrap({seconds,Timeout}), init_high_level_trace(Timeout), - ?line init_condition(Config), - ?line OrigNames = global:registered_names(), + init_condition(Config), + OrigNames = global:registered_names(), - ?line [Cp0, Cp1, Cp2, Cp3, Cp4, Cp5, Cp6, Cp7, Cp8] + [Cp0, Cp1, Cp2, Cp3, Cp4, Cp5, Cp6, Cp7, Cp8] = start_nodes([cp0, cp1, cp2, cp3, cp4, cp5, cp6, cp7, cp8], peer, Config), Nodes = lists:sort([node(), Cp0, Cp1, Cp2, Cp3, Cp4, Cp5, Cp6, Cp7, Cp8]), - ?line wait_for_ready_net(Config), + wait_for_ready_net(Config), Time = msec() + 7000, - ?line rpc_cast(Cp0, ?MODULE, single_node, [Time, Cp8, Config]), - ?line rpc_cast(Cp1, ?MODULE, single_node, [Time, Cp0, Config]), - ?line rpc_cast(Cp2, ?MODULE, single_node, [Time, Cp1, Config]), - ?line rpc_cast(Cp3, ?MODULE, single_node, [Time, Cp2, Config]), - ?line rpc_cast(Cp4, ?MODULE, single_node, [Time, Cp3, Config]), - ?line rpc_cast(Cp5, ?MODULE, single_node, [Time, Cp4, Config]), - ?line rpc_cast(Cp6, ?MODULE, single_node, [Time, Cp5, Config]), - ?line rpc_cast(Cp7, ?MODULE, single_node, [Time, Cp6, Config]), - ?line rpc_cast(Cp8, ?MODULE, single_node, [Time, Cp7, Config]), - - % sleep to make the partitioned net ready - test_server:sleep(Time - msec()), - - ?line pong = net_adm:ping(Cp0), - ?line pong = net_adm:ping(Cp1), - ?line pong = net_adm:ping(Cp2), - ?line pong = net_adm:ping(Cp3), - ?line pong = net_adm:ping(Cp4), - ?line pong = net_adm:ping(Cp5), - ?line pong = net_adm:ping(Cp6), - ?line pong = net_adm:ping(Cp7), - ?line pong = net_adm:ping(Cp8), - - ?line pong = net_adm:ping(Cp0), - ?line pong = net_adm:ping(Cp1), - ?line pong = net_adm:ping(Cp2), - ?line pong = net_adm:ping(Cp3), - ?line pong = net_adm:ping(Cp4), - ?line pong = net_adm:ping(Cp5), - ?line pong = net_adm:ping(Cp6), - ?line pong = net_adm:ping(Cp7), - ?line pong = net_adm:ping(Cp8), - - ?line wait_for_ready_net(Nodes, Config), - - % Just make sure that all nodes have the same picture of all names - ?line check_everywhere(Nodes, single_name, Config), - ?line assert_pid(global:whereis_name(single_name)), - - ?line + rpc_cast(Cp0, ?MODULE, single_node, [Time, Cp8, Config]), + rpc_cast(Cp1, ?MODULE, single_node, [Time, Cp0, Config]), + rpc_cast(Cp2, ?MODULE, single_node, [Time, Cp1, Config]), + rpc_cast(Cp3, ?MODULE, single_node, [Time, Cp2, Config]), + rpc_cast(Cp4, ?MODULE, single_node, [Time, Cp3, Config]), + rpc_cast(Cp5, ?MODULE, single_node, [Time, Cp4, Config]), + rpc_cast(Cp6, ?MODULE, single_node, [Time, Cp5, Config]), + rpc_cast(Cp7, ?MODULE, single_node, [Time, Cp6, Config]), + rpc_cast(Cp8, ?MODULE, single_node, [Time, Cp7, Config]), + + %% sleep to make the partitioned net ready + sleep(Time - msec()), + + pong = net_adm:ping(Cp0), + pong = net_adm:ping(Cp1), + pong = net_adm:ping(Cp2), + pong = net_adm:ping(Cp3), + pong = net_adm:ping(Cp4), + pong = net_adm:ping(Cp5), + pong = net_adm:ping(Cp6), + pong = net_adm:ping(Cp7), + pong = net_adm:ping(Cp8), + + pong = net_adm:ping(Cp0), + pong = net_adm:ping(Cp1), + pong = net_adm:ping(Cp2), + pong = net_adm:ping(Cp3), + pong = net_adm:ping(Cp4), + pong = net_adm:ping(Cp5), + pong = net_adm:ping(Cp6), + pong = net_adm:ping(Cp7), + pong = net_adm:ping(Cp8), + + wait_for_ready_net(Nodes, Config), + + %% Just make sure that all nodes have the same picture of all names + check_everywhere(Nodes, single_name, Config), + assert_pid(global:whereis_name(single_name)), + ?UNTIL(begin {Ns2, []} = rpc:multicall(Nodes, erlang, whereis, [single_name]), @@ -1436,10 +1419,10 @@ ring(Config) when is_list(Config) -> end, 0, Ns2) end), - - ?line assert_pid(global:send(single_name, die)), - ?line ?UNTIL(OrigNames =:= global:registered_names()), + assert_pid(global:send(single_name, die)), + + ?UNTIL(OrigNames =:= global:registered_names()), write_high_level_trace(Config), stop_node(Cp0), @@ -1451,64 +1434,60 @@ ring(Config) when is_list(Config) -> stop_node(Cp6), stop_node(Cp7), stop_node(Cp8), - ?line init_condition(Config), + init_condition(Config), ok. -simple_ring(suite) -> - []; -simple_ring(doc) -> - ["Simpler version of the ring case. Used because there are some", - "distribution problems with many nodes.", - "Make 6 single nodes, all having the same name.", - "Make all ping its predecessor, pinging in a ring.", - "Make sure that there's just one winner."]; +%% Simpler version of the ring case. Used because there are some +%% distribution problems with many nodes. +%% Make 6 single nodes, all having the same name. +%% Make all ping its predecessor, pinging in a ring. +%% Make sure that there's just one winner. simple_ring(Config) when is_list(Config) -> Timeout = 60, ct:timetrap({seconds,Timeout}), init_high_level_trace(Timeout), - ?line init_condition(Config), - ?line OrigNames = global:registered_names(), + init_condition(Config), + OrigNames = global:registered_names(), Names = [cp0, cp1, cp2, cp3, cp4, cp5], - ?line [Cp0, Cp1, Cp2, Cp3, Cp4, Cp5] + [Cp0, Cp1, Cp2, Cp3, Cp4, Cp5] = start_nodes(Names, peer, Config), Nodes = lists:sort([node(), Cp0, Cp1, Cp2, Cp3, Cp4, Cp5]), - ?line wait_for_ready_net(Config), + wait_for_ready_net(Config), Time = msec() + 5000, - ?line rpc_cast(Cp0, ?MODULE, single_node, [Time, Cp5, Config]), - ?line rpc_cast(Cp1, ?MODULE, single_node, [Time, Cp0, Config]), - ?line rpc_cast(Cp2, ?MODULE, single_node, [Time, Cp1, Config]), - ?line rpc_cast(Cp3, ?MODULE, single_node, [Time, Cp2, Config]), - ?line rpc_cast(Cp4, ?MODULE, single_node, [Time, Cp3, Config]), - ?line rpc_cast(Cp5, ?MODULE, single_node, [Time, Cp4, Config]), - - % sleep to make the partitioned net ready - test_server:sleep(Time - msec()), - - ?line pong = net_adm:ping(Cp0), - ?line pong = net_adm:ping(Cp1), - ?line pong = net_adm:ping(Cp2), - ?line pong = net_adm:ping(Cp3), - ?line pong = net_adm:ping(Cp4), - ?line pong = net_adm:ping(Cp5), - - ?line pong = net_adm:ping(Cp0), - ?line pong = net_adm:ping(Cp1), - ?line pong = net_adm:ping(Cp2), - ?line pong = net_adm:ping(Cp3), - ?line pong = net_adm:ping(Cp4), - ?line pong = net_adm:ping(Cp5), - - ?line wait_for_ready_net(Nodes, Config), - - % Just make sure that all nodes have the same picture of all names - ?line check_everywhere(Nodes, single_name, Config), - ?line assert_pid(global:whereis_name(single_name)), - - ?line + rpc_cast(Cp0, ?MODULE, single_node, [Time, Cp5, Config]), + rpc_cast(Cp1, ?MODULE, single_node, [Time, Cp0, Config]), + rpc_cast(Cp2, ?MODULE, single_node, [Time, Cp1, Config]), + rpc_cast(Cp3, ?MODULE, single_node, [Time, Cp2, Config]), + rpc_cast(Cp4, ?MODULE, single_node, [Time, Cp3, Config]), + rpc_cast(Cp5, ?MODULE, single_node, [Time, Cp4, Config]), + + %% sleep to make the partitioned net ready + sleep(Time - msec()), + + pong = net_adm:ping(Cp0), + pong = net_adm:ping(Cp1), + pong = net_adm:ping(Cp2), + pong = net_adm:ping(Cp3), + pong = net_adm:ping(Cp4), + pong = net_adm:ping(Cp5), + + pong = net_adm:ping(Cp0), + pong = net_adm:ping(Cp1), + pong = net_adm:ping(Cp2), + pong = net_adm:ping(Cp3), + pong = net_adm:ping(Cp4), + pong = net_adm:ping(Cp5), + + wait_for_ready_net(Nodes, Config), + + %% Just make sure that all nodes have the same picture of all names + check_everywhere(Nodes, single_name, Config), + assert_pid(global:whereis_name(single_name)), + ?UNTIL(begin {Ns2, []} = rpc:multicall(Nodes, erlang, whereis, [single_name]), @@ -1517,10 +1496,10 @@ simple_ring(Config) when is_list(Config) -> end, 0, Ns2) end), - - ?line assert_pid(global:send(single_name, die)), - ?line ?UNTIL(OrigNames =:= global:registered_names()), + assert_pid(global:send(single_name, die)), + + ?UNTIL(OrigNames =:= global:registered_names()), write_high_level_trace(Config), stop_node(Cp0), @@ -1529,72 +1508,68 @@ simple_ring(Config) when is_list(Config) -> stop_node(Cp3), stop_node(Cp4), stop_node(Cp5), - ?line init_condition(Config), + init_condition(Config), ok. -line(suite) -> - []; -line(doc) -> - ["Make 6 single nodes, all having the same name.", - "Make all ping its predecessor, pinging in a line.", - "Make sure that there's just one winner."]; +%% Make 6 single nodes, all having the same name. +%% Make all ping its predecessor, pinging in a line. +%% Make sure that there's just one winner. line(Config) when is_list(Config) -> Timeout = 60, ct:timetrap({seconds,Timeout}), init_high_level_trace(Timeout), - ?line init_condition(Config), - ?line OrigNames = global:registered_names(), + init_condition(Config), + OrigNames = global:registered_names(), - ?line [Cp0, Cp1, Cp2, Cp3, Cp4, Cp5, Cp6, Cp7, Cp8] + [Cp0, Cp1, Cp2, Cp3, Cp4, Cp5, Cp6, Cp7, Cp8] = start_nodes([cp0, cp1, cp2, cp3, cp4, cp5, cp6, cp7, cp8], peer, Config), Nodes = lists:sort([node(), Cp0, Cp1, Cp2, Cp3, Cp4, Cp5, Cp6, Cp7, Cp8]), - ?line wait_for_ready_net(Config), + wait_for_ready_net(Config), Time = msec() + 7000, - ?line rpc_cast(Cp0, ?MODULE, single_node, - [Time, Cp0, Config]), % ping ourself! - ?line rpc_cast(Cp1, ?MODULE, single_node, [Time, Cp0, Config]), - ?line rpc_cast(Cp2, ?MODULE, single_node, [Time, Cp1, Config]), - ?line rpc_cast(Cp3, ?MODULE, single_node, [Time, Cp2, Config]), - ?line rpc_cast(Cp4, ?MODULE, single_node, [Time, Cp3, Config]), - ?line rpc_cast(Cp5, ?MODULE, single_node, [Time, Cp4, Config]), - ?line rpc_cast(Cp6, ?MODULE, single_node, [Time, Cp5, Config]), - ?line rpc_cast(Cp7, ?MODULE, single_node, [Time, Cp6, Config]), - ?line rpc_cast(Cp8, ?MODULE, single_node, [Time, Cp7, Config]), - - % sleep to make the partitioned net ready - test_server:sleep(Time - msec()), - - ?line pong = net_adm:ping(Cp0), - ?line pong = net_adm:ping(Cp1), - ?line pong = net_adm:ping(Cp2), - ?line pong = net_adm:ping(Cp3), - ?line pong = net_adm:ping(Cp4), - ?line pong = net_adm:ping(Cp5), - ?line pong = net_adm:ping(Cp6), - ?line pong = net_adm:ping(Cp7), - ?line pong = net_adm:ping(Cp8), - - ?line pong = net_adm:ping(Cp0), - ?line pong = net_adm:ping(Cp1), - ?line pong = net_adm:ping(Cp2), - ?line pong = net_adm:ping(Cp3), - ?line pong = net_adm:ping(Cp4), - ?line pong = net_adm:ping(Cp5), - ?line pong = net_adm:ping(Cp6), - ?line pong = net_adm:ping(Cp7), - ?line pong = net_adm:ping(Cp8), - - ?line wait_for_ready_net(Nodes, Config), - - % Just make sure that all nodes have the same picture of all names - ?line check_everywhere(Nodes, single_name, Config), - ?line assert_pid(global:whereis_name(single_name)), - - ?line + rpc_cast(Cp0, ?MODULE, single_node, + [Time, Cp0, Config]), % ping ourself! + rpc_cast(Cp1, ?MODULE, single_node, [Time, Cp0, Config]), + rpc_cast(Cp2, ?MODULE, single_node, [Time, Cp1, Config]), + rpc_cast(Cp3, ?MODULE, single_node, [Time, Cp2, Config]), + rpc_cast(Cp4, ?MODULE, single_node, [Time, Cp3, Config]), + rpc_cast(Cp5, ?MODULE, single_node, [Time, Cp4, Config]), + rpc_cast(Cp6, ?MODULE, single_node, [Time, Cp5, Config]), + rpc_cast(Cp7, ?MODULE, single_node, [Time, Cp6, Config]), + rpc_cast(Cp8, ?MODULE, single_node, [Time, Cp7, Config]), + + %% Sleep to make the partitioned net ready + sleep(Time - msec()), + + pong = net_adm:ping(Cp0), + pong = net_adm:ping(Cp1), + pong = net_adm:ping(Cp2), + pong = net_adm:ping(Cp3), + pong = net_adm:ping(Cp4), + pong = net_adm:ping(Cp5), + pong = net_adm:ping(Cp6), + pong = net_adm:ping(Cp7), + pong = net_adm:ping(Cp8), + + pong = net_adm:ping(Cp0), + pong = net_adm:ping(Cp1), + pong = net_adm:ping(Cp2), + pong = net_adm:ping(Cp3), + pong = net_adm:ping(Cp4), + pong = net_adm:ping(Cp5), + pong = net_adm:ping(Cp6), + pong = net_adm:ping(Cp7), + pong = net_adm:ping(Cp8), + + wait_for_ready_net(Nodes, Config), + + %% Just make sure that all nodes have the same picture of all names + check_everywhere(Nodes, single_name, Config), + assert_pid(global:whereis_name(single_name)), + ?UNTIL(begin {Ns2, []} = rpc:multicall(Nodes, erlang, whereis, [single_name]), @@ -1603,10 +1578,10 @@ line(Config) when is_list(Config) -> end, 0, Ns2) end), - - ?line assert_pid(global:send(single_name, die)), - ?line ?UNTIL(OrigNames =:= global:registered_names()), + assert_pid(global:send(single_name, die)), + + ?UNTIL(OrigNames =:= global:registered_names()), write_high_level_trace(Config), stop_node(Cp0), @@ -1618,65 +1593,61 @@ line(Config) when is_list(Config) -> stop_node(Cp6), stop_node(Cp7), stop_node(Cp8), - ?line init_condition(Config), + init_condition(Config), ok. -simple_line(suite) -> - []; -simple_line(doc) -> - ["Simpler version of the line case. Used because there are some", - "distribution problems with many nodes.", - "Make 6 single nodes, all having the same name.", - "Make all ping its predecessor, pinging in a line.", - "Make sure that there's just one winner."]; +%% Simpler version of the line case. Used because there are some +%% distribution problems with many nodes. +%% Make 6 single nodes, all having the same name. +%% Make all ping its predecessor, pinging in a line. +%% Make sure that there's just one winner. simple_line(Config) when is_list(Config) -> Timeout = 60, ct:timetrap({seconds,Timeout}), init_high_level_trace(Timeout), - ?line init_condition(Config), - ?line OrigNames = global:registered_names(), + init_condition(Config), + OrigNames = global:registered_names(), - ?line [Cp0, Cp1, Cp2, Cp3, Cp4, Cp5] + [Cp0, Cp1, Cp2, Cp3, Cp4, Cp5] = start_nodes([cp0, cp1, cp2, cp3, cp4, cp5], peer, Config), Nodes = lists:sort([node(), Cp0, Cp1, Cp2, Cp3, Cp4, Cp5]), - ?line wait_for_ready_net(Config), + wait_for_ready_net(Config), Time = msec() + 5000, - ?line rpc_cast(Cp0, ?MODULE, single_node, - [Time, Cp0, Config]), % ping ourself! - ?line rpc_cast(Cp1, ?MODULE, single_node, [Time, Cp0, Config]), - ?line rpc_cast(Cp2, ?MODULE, single_node, [Time, Cp1, Config]), - ?line rpc_cast(Cp3, ?MODULE, single_node, [Time, Cp2, Config]), - ?line rpc_cast(Cp4, ?MODULE, single_node, [Time, Cp3, Config]), - ?line rpc_cast(Cp5, ?MODULE, single_node, [Time, Cp4, Config]), - - % sleep to make the partitioned net ready - test_server:sleep(Time - msec()), - - ?line pong = net_adm:ping(Cp0), - ?line pong = net_adm:ping(Cp1), - ?line pong = net_adm:ping(Cp2), - ?line pong = net_adm:ping(Cp3), - ?line pong = net_adm:ping(Cp4), - ?line pong = net_adm:ping(Cp5), - - ?line pong = net_adm:ping(Cp0), - ?line pong = net_adm:ping(Cp1), - ?line pong = net_adm:ping(Cp2), - ?line pong = net_adm:ping(Cp3), - ?line pong = net_adm:ping(Cp4), - ?line pong = net_adm:ping(Cp5), - - ?line wait_for_ready_net(Nodes, Config), - - % Just make sure that all nodes have the same picture of all names - ?line check_everywhere(Nodes, single_name, Config), - ?line assert_pid(global:whereis_name(single_name)), - - ?line + rpc_cast(Cp0, ?MODULE, single_node, + [Time, Cp0, Config]), % ping ourself! + rpc_cast(Cp1, ?MODULE, single_node, [Time, Cp0, Config]), + rpc_cast(Cp2, ?MODULE, single_node, [Time, Cp1, Config]), + rpc_cast(Cp3, ?MODULE, single_node, [Time, Cp2, Config]), + rpc_cast(Cp4, ?MODULE, single_node, [Time, Cp3, Config]), + rpc_cast(Cp5, ?MODULE, single_node, [Time, Cp4, Config]), + + %% sleep to make the partitioned net ready + sleep(Time - msec()), + + pong = net_adm:ping(Cp0), + pong = net_adm:ping(Cp1), + pong = net_adm:ping(Cp2), + pong = net_adm:ping(Cp3), + pong = net_adm:ping(Cp4), + pong = net_adm:ping(Cp5), + + pong = net_adm:ping(Cp0), + pong = net_adm:ping(Cp1), + pong = net_adm:ping(Cp2), + pong = net_adm:ping(Cp3), + pong = net_adm:ping(Cp4), + pong = net_adm:ping(Cp5), + + wait_for_ready_net(Nodes, Config), + + %% Just make sure that all nodes have the same picture of all names + check_everywhere(Nodes, single_name, Config), + assert_pid(global:whereis_name(single_name)), + ?UNTIL(begin {Ns2, []} = rpc:multicall(Nodes, erlang, whereis, [single_name]), @@ -1685,10 +1656,10 @@ simple_line(Config) when is_list(Config) -> end, 0, Ns2) end), - - ?line assert_pid(global:send(single_name, die)), - ?line ?UNTIL(OrigNames =:= global:registered_names()), + assert_pid(global:send(single_name, die)), + + ?UNTIL(OrigNames =:= global:registered_names()), write_high_level_trace(Config), stop_node(Cp0), @@ -1697,82 +1668,74 @@ simple_line(Config) when is_list(Config) -> stop_node(Cp3), stop_node(Cp4), stop_node(Cp5), - ?line init_condition(Config), + init_condition(Config), ok. - -otp_1849(suite) -> []; -otp_1849(doc) -> - ["Test ticket: Global should keep track of all pids that set the same lock."]; + +%% Test ticket: Global should keep track of all pids that set the same lock. otp_1849(Config) when is_list(Config) -> Timeout = 30, ct:timetrap({seconds,Timeout}), init_high_level_trace(Timeout), - ?line init_condition(Config), - ?line {ok, Cp1} = start_node(cp1, Config), - ?line {ok, Cp2} = start_node(cp2, Config), - ?line {ok, Cp3} = start_node(cp3, Config), - - ?line wait_for_ready_net(Config), - - % start procs on each node - ?line Pid1 = rpc:call(Cp1, ?MODULE, start_proc, []), - ?line assert_pid(Pid1), - ?line Pid2 = rpc:call(Cp2, ?MODULE, start_proc, []), - ?line assert_pid(Pid2), - ?line Pid3 = rpc:call(Cp3, ?MODULE, start_proc, []), - ?line assert_pid(Pid3), - - % set a lock on every node - ?line true = req(Pid1, {set_lock2, {test_lock, ?MODULE}, self()}), - ?line true = req(Pid2, {set_lock2, {test_lock, ?MODULE}, self()}), - ?line true = req(Pid3, {set_lock2, {test_lock, ?MODULE}, self()}), - - ?line - ?UNTIL(begin + init_condition(Config), + {ok, Cp1} = start_node(cp1, Config), + {ok, Cp2} = start_node(cp2, Config), + {ok, Cp3} = start_node(cp3, Config), + + wait_for_ready_net(Config), + + %% start procs on each node + Pid1 = rpc:call(Cp1, ?MODULE, start_proc, []), + assert_pid(Pid1), + Pid2 = rpc:call(Cp2, ?MODULE, start_proc, []), + assert_pid(Pid2), + Pid3 = rpc:call(Cp3, ?MODULE, start_proc, []), + assert_pid(Pid3), + + %% set a lock on every node + true = req(Pid1, {set_lock2, {test_lock, ?MODULE}, self()}), + true = req(Pid2, {set_lock2, {test_lock, ?MODULE}, self()}), + true = req(Pid3, {set_lock2, {test_lock, ?MODULE}, self()}), + + ?UNTIL(begin [{test_lock, ?MODULE, Lock1}] = rpc:call(Cp1, ets, tab2list, [global_locks]), 3 =:= length(Lock1) end), - ?line true = req(Pid3, {del_lock2, {test_lock, ?MODULE}, self()}), - ?line + true = req(Pid3, {del_lock2, {test_lock, ?MODULE}, self()}), ?UNTIL(begin [{test_lock, ?MODULE, Lock2}] = rpc:call(Cp1, ets, tab2list, [global_locks]), 2 =:= length(Lock2) end), - ?line true = req(Pid2, {del_lock2, {test_lock, ?MODULE}, self()}), - ?line + true = req(Pid2, {del_lock2, {test_lock, ?MODULE}, self()}), ?UNTIL(begin [{test_lock, ?MODULE, Lock3}] = rpc:call(Cp1, ets, tab2list, [global_locks]), 1 =:= length(Lock3) end), - ?line true = req(Pid1, {del_lock2, {test_lock, ?MODULE}, self()}), - ?line ?UNTIL([] =:= rpc:call(Cp1, ets, tab2list, [global_locks])), + true = req(Pid1, {del_lock2, {test_lock, ?MODULE}, self()}), + ?UNTIL([] =:= rpc:call(Cp1, ets, tab2list, [global_locks])), - ?line true = req(Pid1, {set_lock2, {test_lock, ?MODULE}, self()}), - ?line true = req(Pid2, {set_lock2, {test_lock, ?MODULE}, self()}), - ?line true = req(Pid3, {set_lock2, {test_lock, ?MODULE}, self()}), - ?line false = req(Pid2, {set_lock2, {test_lock, not_valid}, self()}), + true = req(Pid1, {set_lock2, {test_lock, ?MODULE}, self()}), + true = req(Pid2, {set_lock2, {test_lock, ?MODULE}, self()}), + true = req(Pid3, {set_lock2, {test_lock, ?MODULE}, self()}), + false = req(Pid2, {set_lock2, {test_lock, not_valid}, self()}), exit_p(Pid1), - ?line ?UNTIL(begin [{test_lock, ?MODULE, Lock10}] = rpc:call(Cp1, ets, tab2list, [global_locks]), 2 =:= length(Lock10) end), - ?line ?UNTIL(begin [{test_lock, ?MODULE, Lock11}] = rpc:call(Cp2, ets, tab2list, [global_locks]), 2 =:= length(Lock11) end), - ?line ?UNTIL(begin [{test_lock, ?MODULE, Lock12}] = rpc:call(Cp3, ets, tab2list, [global_locks]), @@ -1783,13 +1746,11 @@ otp_1849(Config) when is_list(Config) -> stop_node(Cp1), stop_node(Cp2), stop_node(Cp3), - ?line init_condition(Config), + init_condition(Config), ok. - -otp_3162(suite) -> []; -otp_3162(doc) -> - ["Test ticket: Deadlock in global"]; + +%% Test ticket: Deadlock in global. otp_3162(Config) when is_list(Config) -> StartFun = fun() -> {ok, Cp1} = start_node(cp1, Config), @@ -1803,77 +1764,74 @@ do_otp_3162(StartFun, Config) -> Timeout = 30, ct:timetrap({seconds,Timeout}), init_high_level_trace(Timeout), - ?line init_condition(Config), - ?line [Cp1, Cp2, Cp3] = StartFun(), + init_condition(Config), + [Cp1, Cp2, Cp3] = StartFun(), - ?line wait_for_ready_net(Config), + wait_for_ready_net(Config), - % start procs on each node - ?line Pid1 = rpc:call(Cp1, ?MODULE, start_proc4, [kalle]), - ?line assert_pid(Pid1), - ?line Pid2 = rpc:call(Cp2, ?MODULE, start_proc4, [stina]), - ?line assert_pid(Pid2), - ?line Pid3 = rpc:call(Cp3, ?MODULE, start_proc4, [vera]), - ?line assert_pid(Pid3), + %% start procs on each node + Pid1 = rpc:call(Cp1, ?MODULE, start_proc4, [kalle]), + assert_pid(Pid1), + Pid2 = rpc:call(Cp2, ?MODULE, start_proc4, [stina]), + assert_pid(Pid2), + Pid3 = rpc:call(Cp3, ?MODULE, start_proc4, [vera]), + assert_pid(Pid3), - ?line rpc_disconnect_node(Cp1, Cp2, Config), + rpc_disconnect_node(Cp1, Cp2, Config), - ?line ?UNTIL + ?UNTIL ([Cp3] =:= lists:sort(rpc:call(Cp1, erlang, nodes, [])) -- [node()]), ?UNTIL([kalle, vera] =:= - lists:sort(rpc:call(Cp1, global, registered_names, []))), - ?line ?UNTIL + lists:sort(rpc:call(Cp1, global, registered_names, []))), + ?UNTIL ([Cp3] =:= lists:sort(rpc:call(Cp2, erlang, nodes, [])) -- [node()]), ?UNTIL([stina, vera] =:= - lists:sort(rpc:call(Cp2, global, registered_names, []))), - ?line ?UNTIL + lists:sort(rpc:call(Cp2, global, registered_names, []))), + ?UNTIL ([Cp1, Cp2] =:= - lists:sort(rpc:call(Cp3, erlang, nodes, [])) -- [node()]), + lists:sort(rpc:call(Cp3, erlang, nodes, [])) -- [node()]), ?UNTIL([kalle, stina, vera] =:= - lists:sort(rpc:call(Cp3, global, registered_names, []))), + lists:sort(rpc:call(Cp3, global, registered_names, []))), - ?line pong = rpc:call(Cp2, net_adm, ping, [Cp1]), + pong = rpc:call(Cp2, net_adm, ping, [Cp1]), - ?line ?UNTIL + ?UNTIL ([Cp2, Cp3] =:= - lists:sort(rpc:call(Cp1, erlang, nodes, [])) -- [node()]), - ?line + lists:sort(rpc:call(Cp1, erlang, nodes, [])) -- [node()]), ?UNTIL(begin NN = lists:sort(rpc:call(Cp1, global, registered_names, [])), [kalle, stina, vera] =:= NN end), - ?line ?UNTIL + ?UNTIL ([Cp1, Cp3] =:= - lists:sort(rpc:call(Cp2, erlang, nodes, [])) -- [node()]), + lists:sort(rpc:call(Cp2, erlang, nodes, [])) -- [node()]), ?UNTIL([kalle, stina, vera] =:= - lists:sort(rpc:call(Cp2, global, registered_names, []))), - ?line ?UNTIL + lists:sort(rpc:call(Cp2, global, registered_names, []))), + ?UNTIL ([Cp1, Cp2] =:= - lists:sort(rpc:call(Cp3, erlang, nodes, [])) -- [node()]), + lists:sort(rpc:call(Cp3, erlang, nodes, [])) -- [node()]), ?UNTIL([kalle, stina, vera] =:= - lists:sort(rpc:call(Cp3, global, registered_names, []))), + lists:sort(rpc:call(Cp3, global, registered_names, []))), write_high_level_trace(Config), stop_node(Cp1), stop_node(Cp2), stop_node(Cp3), - ?line init_condition(Config), + init_condition(Config), ok. - -otp_5640(suite) -> []; -otp_5640(doc) -> - ["OTP-5640. 'allow' multiple names for registered processes."]; + +%% OTP-5640. 'allow' multiple names for registered processes. otp_5640(Config) when is_list(Config) -> Timeout = 25, ct:timetrap({seconds,Timeout}), init_high_level_trace(Timeout), init_condition(Config), - ?line {ok, B} = start_node(b, Config), + {ok, B} = start_node(b, Config), - ?line Nodes = lists:sort([node(), B]), - ?line wait_for_ready_net(Nodes, Config), + Nodes = lists:sort([node(), B]), + wait_for_ready_net(Nodes, Config), Server = whereis(global_name_server), ServerB = rpc:call(B, erlang, whereis, [global_name_server]), @@ -1881,50 +1839,50 @@ otp_5640(Config) when is_list(Config) -> Me = self(), Proc = spawn(fun() -> otp_5640_proc(Me) end), - ?line yes = global:register_name(name1, Proc), - ?line no = global:register_name(name2, Proc), + yes = global:register_name(name1, Proc), + no = global:register_name(name2, Proc), - ?line ok = application:set_env(kernel, global_multi_name_action, allow), - ?line yes = global:register_name(name2, Proc), + ok = application:set_env(kernel, global_multi_name_action, allow), + yes = global:register_name(name2, Proc), - test_server:sleep(100), - ?line Proc = global:whereis_name(name1), - ?line Proc = global:whereis_name(name2), - ?line check_everywhere(Nodes, name1, Config), - ?line check_everywhere(Nodes, name2, Config), + ct:sleep(100), + Proc = global:whereis_name(name1), + Proc = global:whereis_name(name2), + check_everywhere(Nodes, name1, Config), + check_everywhere(Nodes, name2, Config), - ?line {monitors_2levels, MonBy1} = mon_by_servers(Proc), - ?line [] = ([Server,Server,ServerB,ServerB] -- MonBy1), - ?line {links,[]} = process_info(Proc, links), - ?line _ = global:unregister_name(name1), + {monitors_2levels, MonBy1} = mon_by_servers(Proc), + [] = ([Server,Server,ServerB,ServerB] -- MonBy1), + {links,[]} = process_info(Proc, links), + _ = global:unregister_name(name1), - test_server:sleep(100), - ?line undefined = global:whereis_name(name1), - ?line Proc = global:whereis_name(name2), - ?line check_everywhere(Nodes, name1, Config), - ?line check_everywhere(Nodes, name2, Config), + ct:sleep(100), + undefined = global:whereis_name(name1), + Proc = global:whereis_name(name2), + check_everywhere(Nodes, name1, Config), + check_everywhere(Nodes, name2, Config), - ?line {monitors_2levels, MonBy2} = mon_by_servers(Proc), - ?line [] = ([Server,ServerB] -- MonBy2), + {monitors_2levels, MonBy2} = mon_by_servers(Proc), + [] = ([Server,ServerB] -- MonBy2), TmpMonBy2 = MonBy2 -- [Server,ServerB], - ?line TmpMonBy2 = TmpMonBy2 -- [Server,ServerB], - ?line {links,[]} = process_info(Proc, links), + TmpMonBy2 = TmpMonBy2 -- [Server,ServerB], + {links,[]} = process_info(Proc, links), - ?line yes = global:register_name(name1, Proc), + yes = global:register_name(name1, Proc), Proc ! die, - test_server:sleep(100), - ?line undefined = global:whereis_name(name1), - ?line undefined = global:whereis_name(name2), - ?line check_everywhere(Nodes, name1, Config), - ?line check_everywhere(Nodes, name2, Config), - ?line {monitors, GMonitors} = process_info(Server, monitors), - ?line false = lists:member({process, Proc}, GMonitors), + ct:sleep(100), + undefined = global:whereis_name(name1), + undefined = global:whereis_name(name2), + check_everywhere(Nodes, name1, Config), + check_everywhere(Nodes, name2, Config), + {monitors, GMonitors} = process_info(Server, monitors), + false = lists:member({process, Proc}, GMonitors), write_high_level_trace(Config), stop_node(B), - ?line init_condition(Config), + init_condition(Config), ok. otp_5640_proc(_Parent) -> @@ -1933,45 +1891,42 @@ otp_5640_proc(_Parent) -> exit(normal) end. -otp_5737(suite) -> []; -otp_5737(doc) -> - ["OTP-5737. set_lock/3 and trans/4 accept Retries = 0."]; +%% OTP-5737. set_lock/3 and trans/4 accept Retries = 0. otp_5737(Config) when is_list(Config) -> Timeout = 25, ct:timetrap({seconds,Timeout}), init_high_level_trace(Timeout), - ?line init_condition(Config), + init_condition(Config), LockId = {?MODULE,self()}, Nodes = [node()], - ?line {'EXIT', _} = (catch global:set_lock(LockId, Nodes, -1)), - ?line {'EXIT', _} = (catch global:set_lock(LockId, Nodes, a)), - ?line true = global:set_lock(LockId, Nodes, 0), + {'EXIT', _} = (catch global:set_lock(LockId, Nodes, -1)), + {'EXIT', _} = (catch global:set_lock(LockId, Nodes, a)), + true = global:set_lock(LockId, Nodes, 0), Time1 = now(), - ?line false = global:set_lock({?MODULE,not_me}, Nodes, 0), - ?line true = timer:now_diff(now(), Time1) < 5000, - ?line _ = global:del_lock(LockId, Nodes), + false = global:set_lock({?MODULE,not_me}, Nodes, 0), + true = timer:now_diff(now(), Time1) < 5000, + _ = global:del_lock(LockId, Nodes), Fun = fun() -> ok end, - ?line {'EXIT', _} = (catch global:trans(LockId, Fun, Nodes, -1)), - ?line {'EXIT', _} = (catch global:trans(LockId, Fun, Nodes, a)), - ?line ok = global:trans(LockId, Fun, Nodes, 0), + {'EXIT', _} = (catch global:trans(LockId, Fun, Nodes, -1)), + {'EXIT', _} = (catch global:trans(LockId, Fun, Nodes, a)), + ok = global:trans(LockId, Fun, Nodes, 0), write_high_level_trace(Config), - ?line init_condition(Config), + init_condition(Config), ok. -otp_6931(suite) -> []; -otp_6931(doc) -> ["OTP-6931. Ignore nodeup when connect_all=false."]; +%% OTP-6931. Ignore nodeup when connect_all=false. otp_6931(Config) when is_list(Config) -> Me = self(), - ?line {ok, CAf} = start_non_connecting_node(ca_false, Config), - ?line ok = rpc:call(CAf, error_logger, add_report_handler, [?MODULE, Me]), - ?line info = rpc:call(CAf, error_logger, warning_map, []), - ?line {global_name_server,CAf} ! {nodeup, fake_node}, + {ok, CAf} = start_non_connecting_node(ca_false, Config), + ok = rpc:call(CAf, error_logger, add_report_handler, [?MODULE, Me]), + info = rpc:call(CAf, error_logger, warning_map, []), + {global_name_server,CAf} ! {nodeup, fake_node}, timer:sleep(100), stop_node(CAf), - receive {nodeup,fake_node} -> test_server:fail({info_report, was, sent}) + receive {nodeup,fake_node} -> ct:fail({info_report, was, sent}) after 1000 -> ok end, ok. @@ -1979,18 +1934,17 @@ otp_6931(Config) when is_list(Config) -> %%%----------------------------------------------------------------- %%% Testing a disconnected node. Not two partitions. %%%----------------------------------------------------------------- -simple_disconnect(suite) -> []; -simple_disconnect(doc) -> ["OTP-5563. Disconnected nodes (not partitions)"]; +%% OTP-5563. Disconnected nodes (not partitions). simple_disconnect(Config) when is_list(Config) -> Timeout = 30, ct:timetrap({seconds,Timeout}), init_high_level_trace(Timeout), - ?line init_condition(Config), - ?line OrigNames = global:registered_names(), + init_condition(Config), + OrigNames = global:registered_names(), %% Three nodes (test_server, n_1, n_2). - ?line [Cp1, Cp2] = Cps = start_nodes([n_1, n_2], peer, Config), - ?line wait_for_ready_net(Config), + [Cp1, Cp2] = Cps = start_nodes([n_1, n_2], peer, Config), + wait_for_ready_net(Config), Nodes = lists:sort([node() | Cps]), @@ -2000,39 +1954,39 @@ simple_disconnect(Config) when is_list(Config) -> Resolver = {no_module, resolve_none}, % will never be called PingNode = Cp2, - ?line {_Pid1, yes} = + {_Pid1, yes} = rpc:call(Cp1, ?MODULE, start_resolver, [Name, Resolver]), - test_server:sleep(100), + ct:sleep(100), %% Disconnect test_server and Cp2. - ?line true = erlang:disconnect_node(Cp2), - test_server:sleep(500), + true = erlang:disconnect_node(Cp2), + ct:sleep(500), %% _Pid is registered on Cp1. The exchange of names between Cp2 and %% test_server sees two identical pids. - ?line pong = net_adm:ping(PingNode), - ?line ?UNTIL(Cps =:= lists:sort(nodes())), + pong = net_adm:ping(PingNode), + ?UNTIL(Cps =:= lists:sort(nodes())), - ?line {_, Trace0} = collect_tracers(Nodes), - ?line Resolvers = [P || {_Node,new_resolver,{pid,P}} <- Trace0], - ?line lists:foreach(fun(P) -> P ! die end, Resolvers), - ?line lists:foreach(fun(P) -> wait_for_exit(P) end, Resolvers), - ?line check_everywhere(Nodes, Name, Config), - ?line undefined = global:whereis_name(Name), + {_, Trace0} = collect_tracers(Nodes), + Resolvers = [P || {_Node,new_resolver,{pid,P}} <- Trace0], + lists:foreach(fun(P) -> P ! die end, Resolvers), + lists:foreach(fun(P) -> wait_for_exit(P) end, Resolvers), + check_everywhere(Nodes, Name, Config), + undefined = global:whereis_name(Name), - ?line {_, Trace1} = collect_tracers(Nodes), + {_, Trace1} = collect_tracers(Nodes), Trace = Trace0 ++ Trace1, - ?line [] = [foo || {_, resolve_none, _, _} <- Trace], + [] = [foo || {_, resolve_none, _, _} <- Trace], - ?line Gs = name_servers(Nodes), - ?line [_, _, _] = monitored_by_node(Trace, Gs), + Gs = name_servers(Nodes), + [_, _, _] = monitored_by_node(Trace, Gs), lists:foreach(fun(N) -> rpc:call(N, ?MODULE, stop_tracer, []) end, Nodes), - ?line OrigNames = global:registered_names(), + OrigNames = global:registered_names(), write_high_level_trace(Config), stop_nodes(Cps), - ?line init_condition(Config), + init_condition(Config), ok. %% Not used right now. @@ -2051,7 +2005,7 @@ simple_dis(Nodes0, Name, Resolver, Config) -> simple_dis_node(_Node, DisNodes, _Name, _Resolver, Config) -> lists:foreach( fun(OtherNode) -> _ = erlang:disconnect_node(OtherNode) end, DisNodes), - ?line ?UNTIL(DisNodes -- nodes() =:= DisNodes), + ?UNTIL(DisNodes -- nodes() =:= DisNodes), ok. @@ -2071,19 +2025,18 @@ simple_dis_node(_Node, DisNodes, _Name, _Resolver, Config) -> -define(RES(F), {F, fun ?MODULE:F/3}). -simple_resolve(suite) -> []; -simple_resolve(doc) -> ["OTP-5563. Partitions and names."]; +%% OTP-5563. Partitions and names. simple_resolve(Config) when is_list(Config) -> Timeout = 360, ct:timetrap({seconds,Timeout}), init_high_level_trace(Timeout), - ?line init_condition(Config), - ?line OrigNames = global:registered_names(), + init_condition(Config), + OrigNames = global:registered_names(), - ?line [N1, A2, Z2] = Cps = start_nodes([n_1, a_2, z_2], peer, Config), + [N1, A2, Z2] = Cps = start_nodes([n_1, a_2, z_2], peer, Config), Nodes = lists:sort([node() | Cps]), - ?line wait_for_ready_net(Config), - + wait_for_ready_net(Config), + lists:foreach(fun(N) -> rpc:call(N, ?MODULE, start_tracer, []) end, Nodes), @@ -2192,20 +2145,19 @@ simple_resolve(Config) when is_list(Config) -> %% then a new attempt (nodeup etc.) is made. This time the %% resolver does not disconnect any node. res(?RES(disconnect_first), Cps, Cf#cf{link = Z2, n2 = Z2, - nodes = [node(), N1, A2, Z2]}), + nodes = [node(), N1, A2, Z2]}), - ?line lists:foreach(fun(N) -> - rpc:call(N, ?MODULE, stop_tracer, []) - end, Nodes), + lists:foreach(fun(N) -> + rpc:call(N, ?MODULE, stop_tracer, []) + end, Nodes), - ?line OrigNames = global:registered_names(), + OrigNames = global:registered_names(), write_high_level_trace(Config), stop_nodes(Cps), - ?line init_condition(Config), + init_condition(Config), ok. -simple_resolve2(suite) -> []; -simple_resolve2(doc) -> ["OTP-5563. Partitions and names."]; +%% OTP-5563. Partitions and names. simple_resolve2(Config) when is_list(Config) -> %% Continuation of simple_resolve. Of some reason it did not %% always work to re-start z_2. "Cannot be a global bug." @@ -2213,13 +2165,13 @@ simple_resolve2(Config) when is_list(Config) -> Timeout = 30, ct:timetrap({seconds,Timeout}), init_high_level_trace(Timeout), - ?line init_condition(Config), - ?line OrigNames = global:registered_names(), + init_condition(Config), + OrigNames = global:registered_names(), - ?line [N1, A2, Z2] = Cps = start_nodes([n_1, a_2, z_2], peer, Config), - ?line wait_for_ready_net(Config), + [N1, A2, Z2] = Cps = start_nodes([n_1, a_2, z_2], peer, Config), + wait_for_ready_net(Config), Nodes = lists:sort([node() | Cps]), - + lists:foreach(fun(N) -> rpc:call(N, ?MODULE, start_tracer, []) end, Nodes), @@ -2229,33 +2181,32 @@ simple_resolve2(Config) when is_list(Config) -> %% Halt z_2. res(?RES(halt_second), Cps, Cf#cf{link = N1, n1 = N1, n2 = Z2, ping = A2, - nodes = [node(), N1, A2], n_res = 1}), + nodes = [node(), N1, A2], n_res = 1}), - ?line lists:foreach(fun(N) -> - rpc:call(N, ?MODULE, stop_tracer, []) - end, Nodes), + lists:foreach(fun(N) -> + rpc:call(N, ?MODULE, stop_tracer, []) + end, Nodes), - ?line OrigNames = global:registered_names(), + OrigNames = global:registered_names(), write_high_level_trace(Config), stop_nodes(Cps), % Not all nodes may be present, but it works anyway. - ?line init_condition(Config), + init_condition(Config), ok. -simple_resolve3(suite) -> []; -simple_resolve3(doc) -> ["OTP-5563. Partitions and names."]; +%% OTP-5563. Partitions and names. simple_resolve3(Config) when is_list(Config) -> %% Continuation of simple_resolve. Timeout = 30, ct:timetrap({seconds,Timeout}), init_high_level_trace(Timeout), - ?line init_condition(Config), - ?line OrigNames = global:registered_names(), + init_condition(Config), + OrigNames = global:registered_names(), - ?line [N1, A2, Z2] = Cps = start_nodes([n_1, a_2, z_2], peer, Config), - ?line wait_for_ready_net(Config), + [N1, A2, Z2] = Cps = start_nodes([n_1, a_2, z_2], peer, Config), + wait_for_ready_net(Config), Nodes = lists:sort([node() | Cps]), - + lists:foreach(fun(N) -> rpc:call(N, ?MODULE, start_tracer, []) end, Nodes), @@ -2266,27 +2217,27 @@ simple_resolve3(Config) when is_list(Config) -> %% Halt a_2. res(?RES(halt_second), Cps, Cf#cf{link = node(), n2 = A2, nodes = [node(), N1], n_res = 1}), - - ?line lists:foreach(fun(N) -> - rpc:call(N, ?MODULE, stop_tracer, []) - end, Nodes), - ?line OrigNames = global:registered_names(), + lists:foreach(fun(N) -> + rpc:call(N, ?MODULE, stop_tracer, []) + end, Nodes), + + OrigNames = global:registered_names(), write_high_level_trace(Config), stop_nodes(Cps), % Not all nodes may be present, but it works anyway. - ?line init_condition(Config), + init_condition(Config), ok. res({Res,Resolver}, [N1, A2, Z2], Cf) -> %% Note: there are no links anymore, but monitors. #cf{link = LinkedNode, ping = PingNode, n1 = Res1, n2 = OtherNode, nodes = Nodes0, n_res = NRes, config = Config} = Cf, - ?t:format("~n~nResolver: ~p", [Res]), - ?t:format(" Registered on partition 1: ~p", [Res1]), - ?t:format(" Registered on partition 2: ~p", [OtherNode]), - ?t:format(" Pinged node: ~p", [PingNode]), - ?t:format(" Linked node: ~p", [LinkedNode]), - ?t:format(" Expected # resolvers: ~p", [NRes]), + io:format("~n~nResolver: ~p", [Res]), + io:format(" Registered on partition 1: ~p", [Res1]), + io:format(" Registered on partition 2: ~p", [OtherNode]), + io:format(" Pinged node: ~p", [PingNode]), + io:format(" Linked node: ~p", [LinkedNode]), + io:format(" Expected # resolvers: ~p", [NRes]), Nodes = lists:sort(Nodes0), T1 = node(), Part1 = [T1, N1], @@ -2298,67 +2249,67 @@ res({Res,Resolver}, [N1, A2, Z2], Cf) -> %% expected monitors remain between registered processes and the %% global_name_server. - ?line rpc_cast(OtherNode, - ?MODULE, - part_2_2, - [Config, Part1, Part2, [{Name, Resolver}]]), - ?line ?UNTIL(is_ready_partition(Config)), - ?line {_Pid1, yes} = + rpc_cast(OtherNode, + ?MODULE, + part_2_2, + [Config, Part1, Part2, [{Name, Resolver}]]), + ?UNTIL(is_ready_partition(Config)), + {_Pid1, yes} = rpc:call(Res1, ?MODULE, start_resolver, [Name, Resolver]), - ?line pong = net_adm:ping(PingNode), - ?line wait_for_ready_net(Nodes, Config), + pong = net_adm:ping(PingNode), + wait_for_ready_net(Nodes, Config), - ?line check_everywhere(Nodes, Name, Config), - ?line case global:whereis_name(Name) of - undefined when LinkedNode =:= none -> ok; - Pid -> assert_pid(Pid) - end, + check_everywhere(Nodes, Name, Config), + case global:whereis_name(Name) of + undefined when LinkedNode =:= none -> ok; + Pid -> assert_pid(Pid) + end, - ?line {_, Trace0} = collect_tracers(Nodes), - ?line Resolvers = [P || {_Node,new_resolver,{pid,P}} <- Trace0], + {_, Trace0} = collect_tracers(Nodes), + Resolvers = [P || {_Node,new_resolver,{pid,P}} <- Trace0], - ?line NRes = length(Resolvers), + NRes = length(Resolvers), %% Wait for extra monitor processes to be created. %% This applies as long as global:do_monitor/1 spawns processes. %% (Some day monitor() will be truly synchronous.) - test_server:sleep(100), + ct:sleep(100), - ?line lists:foreach(fun(P) -> P ! die end, Resolvers), - ?line lists:foreach(fun(P) -> wait_for_exit(P) end, Resolvers), + lists:foreach(fun(P) -> P ! die end, Resolvers), + lists:foreach(fun(P) -> wait_for_exit(P) end, Resolvers), - ?line check_everywhere(Nodes, Name, Config), - ?line undefined = global:whereis_name(Name), + check_everywhere(Nodes, Name, Config), + undefined = global:whereis_name(Name), %% Wait for monitors to remove names. - test_server:sleep(100), + ct:sleep(100), - ?line {_, Trace1} = collect_tracers(Nodes), + {_, Trace1} = collect_tracers(Nodes), Trace = Trace0 ++ Trace1, - ?line Gs = name_servers([T1, N1, A2, Z2]), - ?line MonitoredByNode = monitored_by_node(Trace, Gs), - ?line MonitoredBy = [M || {_N,M} <- MonitoredByNode], - + Gs = name_servers([T1, N1, A2, Z2]), + MonitoredByNode = monitored_by_node(Trace, Gs), + MonitoredBy = [M || {_N,M} <- MonitoredByNode], + X = MonitoredBy -- Gs, LengthGs = length(Gs), - ?line case MonitoredBy of - [] when LinkedNode =:= none -> ok; - Gs -> ok; - _ when LengthGs < 4, X =:= [] -> ok; - _ -> ?t:format("ERROR:~nMonitoredBy ~p~n" - "global_name_servers ~p~n", - [MonitoredByNode, Gs]), - ?t:fail(monitor_mismatch) - end, + case MonitoredBy of + [] when LinkedNode =:= none -> ok; + Gs -> ok; + _ when LengthGs < 4, X =:= [] -> ok; + _ -> io:format("ERROR:~nMonitoredBy ~p~n" + "global_name_servers ~p~n", + [MonitoredByNode, Gs]), + ct:fail(monitor_mismatch) + end, ok. name_servers(Nodes) -> lists:sort([rpc:call(N, erlang, whereis, [global_name_server]) || N <- Nodes, pong =:= net_adm:ping(N)]). - + monitored_by_node(Trace, Servers) -> lists:sort([{node(M),M} || {_Node,_P,died,{monitors_2levels,ML}} <- Trace, @@ -2370,7 +2321,7 @@ part_2_2(Config, Part1, Part2, NameResolvers) -> make_partition(Config, Part1, Part2), lists:foreach (fun({Name, Resolver}) -> - ?line {Pid2, yes} = start_resolver(Name, Resolver), + {Pid2, yes} = start_resolver(Name, Resolver), trace_message({node(), part_2_2, nodes(), {pid2,Pid2}}) end, NameResolvers). @@ -2395,7 +2346,7 @@ exit_resolver(name, _Pid1, _Pid2) -> lock_resolver(name, Pid1, _Pid2) -> Id = {?MODULE, self()}, Nodes = [node()], - ?line true = global:set_lock(Id, Nodes), + true = global:set_lock(Id, Nodes), _ = global:del_lock(Id, Nodes), Pid1. @@ -2425,7 +2376,7 @@ start_resolver(Name, Resolver) -> receive {Pid, Res} -> {Pid, Res} end. - + init_resolver(Parent, Name, Resolver) -> X = global:register_name(Name, self(), Resolver), Parent ! {self(), X}, @@ -2454,18 +2405,17 @@ mon_by_servers(Proc) -> -define(REGNAME, contact_a_2). -leftover_name(suite) -> []; -leftover_name(doc) -> ["OTP-5563. Bug: nodedown while synching."]; +%% OTP-5563. Bug: nodedown while synching. leftover_name(Config) when is_list(Config) -> Timeout = 30, ct:timetrap({seconds,Timeout}), init_high_level_trace(Timeout), - ?line init_condition(Config), - ?line OrigNames = global:registered_names(), - ?line [N1, A2, Z2] = Cps = start_nodes([n_1, a_2, z_2], peer, Config), + init_condition(Config), + OrigNames = global:registered_names(), + [N1, A2, Z2] = Cps = start_nodes([n_1, a_2, z_2], peer, Config), Nodes = lists:sort([node() | Cps]), - ?line wait_for_ready_net(Config), - + wait_for_ready_net(Config), + lists:foreach(fun(N) -> rpc:call(N, ?MODULE, start_tracer, []) end, Nodes), @@ -2473,20 +2423,20 @@ leftover_name(Config) when is_list(Config) -> Name = name, % registered on a_2 ResName = resolved_name, % registered on n_1 and a_2 %% - ?line _Pid = ping_a_2_fun(?REGNAME, N1, A2), + _Pid = ping_a_2_fun(?REGNAME, N1, A2), T1 = node(), Part1 = [T1, N1], Part2 = [A2, Z2], NoResolver = {no_module, resolve_none}, Resolver = fun contact_a_2/3, - ?line rpc_cast(A2, - ?MODULE, part_2_2, [Config, - Part1, - Part2, - [{Name, NoResolver}, - {ResName, Resolver}]]), - ?line ?UNTIL(is_ready_partition(Config)), + rpc_cast(A2, + ?MODULE, part_2_2, [Config, + Part1, + Part2, + [{Name, NoResolver}, + {ResName, Resolver}]]), + ?UNTIL(is_ready_partition(Config)), %% resolved_name is resolved to run on a_2, an insert operation is %% sent to n_1. The resolver function halts a_2, but the nodedown @@ -2495,36 +2445,36 @@ leftover_name(Config) when is_list(Config) -> %% delayed). Unless "artificial" nodedown messages are sent the %% name would linger on indefinitely. [There is no test case for %% the situation that no nodedown message at all is sent.] - ?line {_Pid1, yes} = + {_Pid1, yes} = rpc:call(N1, ?MODULE, start_resolver, [ResName, fun contact_a_2/3]), - test_server:sleep(1000), + ct:sleep(1000), - ?line trace_message({node(), pinging, z_2}), - ?line pong = net_adm:ping(Z2), - ?line ?UNTIL((Nodes -- [A2]) =:= lists:sort(?NODES)), - ?t:sleep(1000), + trace_message({node(), pinging, z_2}), + pong = net_adm:ping(Z2), + ?UNTIL((Nodes -- [A2]) =:= lists:sort(?NODES)), + ct:sleep(1000), - ?line {_,Trace0} = collect_tracers(Nodes), + {_,Trace0} = collect_tracers(Nodes), - ?line Resolvers = [P || {_Node,new_resolver,{pid,P}} <- Trace0], - ?line lists:foreach(fun(P) -> P ! die end, Resolvers), - ?line lists:foreach(fun(P) -> wait_for_exit(P) end, Resolvers), + Resolvers = [P || {_Node,new_resolver,{pid,P}} <- Trace0], + lists:foreach(fun(P) -> P ! die end, Resolvers), + lists:foreach(fun(P) -> wait_for_exit(P) end, Resolvers), - ?line lists:foreach(fun(N) -> - rpc:call(N, ?MODULE, stop_tracer, []) - end, Nodes), + lists:foreach(fun(N) -> + rpc:call(N, ?MODULE, stop_tracer, []) + end, Nodes), - ?line ?UNTIL(OrigNames =:= global:registered_names()), + ?UNTIL(OrigNames =:= global:registered_names()), write_high_level_trace(Config), stop_nodes(Cps), - ?line init_condition(Config), + init_condition(Config), ok. %% Runs on n_1 contact_a_2(resolved_name, Pid1, Pid2) -> trace_message({node(), ?REGNAME, {pid1,Pid1}, {pid2,Pid2}, - {node1,node(Pid1)}, {node2,node(Pid2)}}), + {node1,node(Pid1)}, {node2,node(Pid2)}}), ?REGNAME ! doit, Pid2. @@ -2542,15 +2492,14 @@ ping_a_2(RegName, N1, A2) -> {nodedown, A2} -> ok end end. - + halt_node(Node) -> rpc:call(Node, erlang, halt, []). %%%----------------------------------------------------------------- %%% Testing re-registration of a name. %%%----------------------------------------------------------------- -re_register_name(suite) -> []; -re_register_name(doc) -> ["OTP-5563. Name is re-registered."]; +%% OTP-5563. Name is re-registered. re_register_name(Config) when is_list(Config) -> %% When re-registering a name the link to the old pid used to %% linger on. Don't think is was a serious bug though--some memory @@ -2559,18 +2508,18 @@ re_register_name(Config) when is_list(Config) -> Timeout = 15, ct:timetrap({seconds,Timeout}), init_high_level_trace(Timeout), - ?line init_condition(Config), + init_condition(Config), Me = self(), Pid1 = spawn(fun() -> proc(Me) end), - ?line yes = global:register_name(name, Pid1), + yes = global:register_name(name, Pid1), Pid2 = spawn(fun() -> proc(Me) end), - ?line _ = global:re_register_name(name, Pid2), + _ = global:re_register_name(name, Pid2), Pid2 ! die, Pid1 ! die, receive {Pid1, MonitoredBy1} -> [] = MonitoredBy1 end, receive {Pid2, MonitoredBy2} -> [_] = MonitoredBy2 end, - ?line _ = global:unregister_name(name), - ?line init_condition(Config), + _ = global:unregister_name(name), + init_condition(Config), ok. proc(Parent) -> @@ -2582,15 +2531,14 @@ proc(Parent) -> %%%----------------------------------------------------------------- %%% %%%----------------------------------------------------------------- -name_exit(suite) -> []; -name_exit(doc) -> ["OTP-5563. Registered process dies."]; +%% OTP-5563. Registered process dies. name_exit(Config) when is_list(Config) -> StartFun = fun() -> {ok, N1} = start_node_rel(n_1, this, Config), {ok, N2} = start_node_rel(n_2, this, Config), [N1, N2] end, - ?t:format("Test of current release~n"), + io:format("Test of current release~n"), do_name_exit(StartFun, current, Config). do_name_exit(StartFun, Version, Config) -> @@ -2606,17 +2554,17 @@ do_name_exit(StartFun, Version, Config) -> Timeout = 60, ct:timetrap({seconds,Timeout}), init_high_level_trace(Timeout), - ?line init_condition(Config), - ?line OrigNames = global:registered_names(), + init_condition(Config), + OrigNames = global:registered_names(), %% Three nodes (test_server, n_1, n_2). - ?line Cps = StartFun(), + Cps = StartFun(), Nodes = lists:sort([node() | Cps]), - ?line wait_for_ready_net(Config), + wait_for_ready_net(Config), lists:foreach(fun(N) -> rpc:call(N, ?MODULE, start_tracer, []) end,Nodes), Name = name, - ?line {Pid, yes} = start_proc(Name), + {Pid, yes} = start_proc(Name), Me = self(), LL = spawn(fun() -> long_lock(Me) end), @@ -2627,23 +2575,23 @@ do_name_exit(StartFun, Version, Config) -> Pid ! die, wait_for_exit_fast(Pid), - ?t:sleep(100), + ct:sleep(100), %% Name has been removed from node()'s table, but nowhere else %% since there is a lock on 'global'. {R1,[]} = rpc:multicall(Nodes, global, whereis_name, [Name]), - ?line case Version of - old -> [_,_] = lists:usort(R1); - current -> [undefined, undefined, undefined] = R1 - end, - ?t:sleep(3000), - ?line check_everywhere(Nodes, Name, Config), + case Version of + old -> [_,_] = lists:usort(R1); + current -> [undefined, undefined, undefined] = R1 + end, + ct:sleep(3000), + check_everywhere(Nodes, Name, Config), lists:foreach(fun(N) -> rpc:call(N, ?MODULE, stop_tracer, []) end, Nodes), - ?line OrigNames = global:registered_names(), + OrigNames = global:registered_names(), exit(LL, kill), write_high_level_trace(Config), stop_nodes(Cps), - ?line init_condition(Config), + init_condition(Config), ok. long_lock(Parent) -> @@ -2656,17 +2604,16 @@ long_lock(Parent) -> %%%----------------------------------------------------------------- %%% Testing the support for external nodes (cnodes) %%%----------------------------------------------------------------- -external_nodes(suite) -> []; -external_nodes(doc) -> ["OTP-5563. External nodes (cnodes)."]; +%% OTP-5563. External nodes (cnodes). external_nodes(Config) when is_list(Config) -> Timeout = 30, ct:timetrap({seconds,Timeout}), init_high_level_trace(Timeout), - ?line init_condition(Config), - ?line OrigNames = global:registered_names(), + init_condition(Config), + OrigNames = global:registered_names(), - ?line [NodeB, NodeC] = start_nodes([b, c], peer, Config), - ?line wait_for_ready_net(Config), + [NodeB, NodeC] = start_nodes([b, c], peer, Config), + wait_for_ready_net(Config), %% Nodes = ?NODES, %% lists:foreach(fun(N) -> rpc:call(N, ?MODULE, start_tracer, []) end, @@ -2675,75 +2622,75 @@ external_nodes(Config) when is_list(Config) -> %% Two partitions: [test_server] and [b, c]. %% c registers an external name on b - ?line rpc_cast(NodeB, ?MODULE, part_ext, - [Config, node(), NodeC, Name]), - ?line ?UNTIL(is_ready_partition(Config)), + rpc_cast(NodeB, ?MODULE, part_ext, + [Config, node(), NodeC, Name]), + ?UNTIL(is_ready_partition(Config)), - ?line pong = net_adm:ping(NodeB), - ?line ?UNTIL([NodeB, NodeC] =:= lists:sort(nodes())), - ?line wait_for_ready_net(Config), + pong = net_adm:ping(NodeB), + ?UNTIL([NodeB, NodeC] =:= lists:sort(nodes())), + wait_for_ready_net(Config), - ?line Cpid = rpc:call(NodeC, erlang, whereis, [Name]), + Cpid = rpc:call(NodeC, erlang, whereis, [Name]), ExternalName = [{name,Cpid,NodeB}], - ?line ExternalName = get_ext_names(), - ?line ExternalName = rpc:call(NodeB, gen_server, call, - [global_name_server, get_names_ext]), - ?line ExternalName = rpc:call(NodeC, gen_server, call, - [global_name_server, get_names_ext]), - - ?line [_] = cnode_links(Cpid), - ?line [_,_,_] = cnode_monitored_by(Cpid), - ?line no = global:register_name(Name, self()), - ?line yes = global:re_register_name(Name, self()), - ?line ?UNTIL([] =:= cnode_monitored_by(Cpid)), - ?line ?UNTIL([] =:= cnode_links(Cpid)), - ?line [] = gen_server:call(global_name_server, get_names_ext, infinity), - - ?line Cpid ! {register, self(), Name}, - ?line receive {Cpid, Reply1} -> no = Reply1 end, - ?line _ = global:unregister_name(Name), - test_server:sleep(1000), - ?line Cpid ! {register, self(), Name}, - ?line ?UNTIL(length(get_ext_names()) =:= 1), - ?line receive {Cpid, Reply2} -> yes = Reply2 end, - - ?line Cpid ! {unregister, self(), Name}, - ?line ?UNTIL(length(get_ext_names()) =:= 0), - ?line receive {Cpid, Reply3} -> ok = Reply3 end, + ExternalName = get_ext_names(), + ExternalName = rpc:call(NodeB, gen_server, call, + [global_name_server, get_names_ext]), + ExternalName = rpc:call(NodeC, gen_server, call, + [global_name_server, get_names_ext]), + + [_] = cnode_links(Cpid), + [_,_,_] = cnode_monitored_by(Cpid), + no = global:register_name(Name, self()), + yes = global:re_register_name(Name, self()), + ?UNTIL([] =:= cnode_monitored_by(Cpid)), + ?UNTIL([] =:= cnode_links(Cpid)), + [] = gen_server:call(global_name_server, get_names_ext, infinity), + + Cpid ! {register, self(), Name}, + receive {Cpid, Reply1} -> no = Reply1 end, + _ = global:unregister_name(Name), + ct:sleep(1000), + Cpid ! {register, self(), Name}, + ?UNTIL(length(get_ext_names()) =:= 1), + receive {Cpid, Reply2} -> yes = Reply2 end, + + Cpid ! {unregister, self(), Name}, + ?UNTIL(length(get_ext_names()) =:= 0), + receive {Cpid, Reply3} -> ok = Reply3 end, Cpid ! die, - ?line ?UNTIL(OrigNames =:= global:registered_names()), - ?line [] = get_ext_names(), - ?line [] = rpc:call(NodeB, gen_server, call, - [global_name_server, get_names_ext]), - ?line [] = rpc:call(NodeC, gen_server, call, - [global_name_server, get_names_ext]), + ?UNTIL(OrigNames =:= global:registered_names()), + [] = get_ext_names(), + [] = rpc:call(NodeB, gen_server, call, + [global_name_server, get_names_ext]), + [] = rpc:call(NodeC, gen_server, call, + [global_name_server, get_names_ext]), - ?line Cpid2 = erlang:spawn(NodeC, fun() -> cnode_proc(NodeB) end), - ?line Cpid2 ! {register, self(), Name}, - ?line receive {Cpid2, Reply4} -> yes = Reply4 end, + Cpid2 = erlang:spawn(NodeC, fun() -> cnode_proc(NodeB) end), + Cpid2 ! {register, self(), Name}, + receive {Cpid2, Reply4} -> yes = Reply4 end, %% It could be a bug that Cpid2 is linked to 'global_name_server' %% at node 'b'. The effect: Cpid2 dies when node 'b' crashes. stop_node(NodeB), - ?line ?UNTIL(OrigNames =:= global:registered_names()), - ?line [] = get_ext_names(), - ?line [] = rpc:call(NodeC, gen_server, call, - [global_name_server, get_names_ext]), + ?UNTIL(OrigNames =:= global:registered_names()), + [] = get_ext_names(), + [] = rpc:call(NodeC, gen_server, call, + [global_name_server, get_names_ext]), - %% ?line {_, Trace} = collect_tracers(Nodes), + %% {_, Trace} = collect_tracers(Nodes), %% lists:foreach(fun(M) -> erlang:display(M) end, Trace), ThisNode = node(), - ?line Cpid3 = erlang:spawn(NodeC, fun() -> cnode_proc(ThisNode) end), - ?line Cpid3 ! {register, self(), Name}, - ?line receive {Cpid3, Reply5} -> yes = Reply5 end, + Cpid3 = erlang:spawn(NodeC, fun() -> cnode_proc(ThisNode) end), + Cpid3 ! {register, self(), Name}, + receive {Cpid3, Reply5} -> yes = Reply5 end, - ?line ?UNTIL(length(get_ext_names()) =:= 1), + ?UNTIL(length(get_ext_names()) =:= 1), stop_node(NodeC), - ?line ?UNTIL(length(get_ext_names()) =:= 0), + ?UNTIL(length(get_ext_names()) =:= 0), - ?line init_condition(Config), + init_condition(Config), ok. get_ext_names() -> @@ -2790,19 +2737,16 @@ cnode_proc(E) -> cnode_proc(E). -many_nodes(suite) -> - []; -many_nodes(doc) -> - ["OTP-5770. Start many nodes. Make them connect at the same time."]; +%% OTP-5770. Start many nodes. Make them connect at the same time. many_nodes(Config) when is_list(Config) -> Timeout = 240, ct:timetrap({seconds,Timeout}), init_high_level_trace(Timeout), - ?line init_condition(Config), - ?line OrigNames = global:registered_names(), + init_condition(Config), + OrigNames = global:registered_names(), {Rels, N_cps} = - case ?t:os_type() of + case test_server:os_type() of {unix, Osname} when Osname =:= linux; Osname =:= openbsd; Osname =:= darwin -> @@ -2813,12 +2757,12 @@ many_nodes(Config) when is_list(Config) -> _ -> {node_rel(1, 32, this), 32} end, - ?line Cps = [begin {ok, Cp} = start_node_rel(Name, Rel, Config), Cp end || - {Name,Rel} <- Rels], + Cps = [begin {ok, Cp} = start_node_rel(Name, Rel, Config), Cp end || + {Name,Rel} <- Rels], Nodes = lists:sort(?NODES), - ?line wait_for_ready_net(Nodes, Config), + wait_for_ready_net(Nodes, Config), - ?line Dir = ?config(priv_dir, Config), + Dir = proplists:get_value(priv_dir, Config), GoFile = filename:join([Dir, "go.txt"]), file:delete(GoFile), @@ -2829,34 +2773,34 @@ many_nodes(Config) when is_list(Config) -> file:delete(File), rpc_cast(N, ?MODULE, isolated_node, [File, GoFile, Cps, Config]) end, - ?line lists:foreach(IsoFun, CpsFiles), - - ?line all_nodes_files(CpsFiles, "isolated", Config), - ?line Time = msec(), - ?line sync_until(), + lists:foreach(IsoFun, CpsFiles), + + all_nodes_files(CpsFiles, "isolated", Config), + Time = msec(), + sync_until(), erlang:display(ready_to_go), - ?line touch(GoFile, "go"), - ?line all_nodes_files(CpsFiles, "done", Config), - ?line Time2 = msec(), + touch(GoFile, "go"), + all_nodes_files(CpsFiles, "done", Config), + Time2 = msec(), - ?line lists:foreach(fun(N) -> pong = net_adm:ping(N) end, Cps), + lists:foreach(fun(N) -> pong = net_adm:ping(N) end, Cps), - ?line wait_for_ready_net(Config), + wait_for_ready_net(Config), write_high_level_trace(Config), % The test succeeded, but was it slow? - ?line lists:foreach(fun({_N, File}) -> file:delete(File) end, CpsFiles), - ?line file:delete(GoFile), + lists:foreach(fun({_N, File}) -> file:delete(File) end, CpsFiles), + file:delete(GoFile), - ?line ?UNTIL(OrigNames =:= global:registered_names()), + ?UNTIL(OrigNames =:= global:registered_names()), write_high_level_trace(Config), - ?line stop_nodes(Cps), - ?line init_condition(Config), + stop_nodes(Cps), + init_condition(Config), Diff = Time2 - Time, Return = lists:flatten(io_lib:format("~w nodes took ~w ms", [N_cps, Diff])), erlang:display({{nodes,N_cps},{time,Diff}}), - ?t:format("~s~n", [Return]), + io:format("~s~n", [Return]), {comment, Return}. node_rel(From, To, Rel) -> @@ -2882,7 +2826,7 @@ isolated_node(File, GoFile, Nodes, Config) -> touch(File, "got_go"), lists:foreach(fun(N) -> _ = net_adm:ping(N) end, shuffle(Nodes)), touch(File, "pinged"), - ?line ?UNTIL((Ns -- get_known(node())) =:= []), + ?UNTIL((Ns -- get_known(node())) =:= []), touch(File, "done"). touch(File, List) -> @@ -2930,19 +2874,17 @@ sync_until(LogFile) -> timer:sleep(Time). shuffle(L) -> - [E || {_, E} <- lists:keysort(1, [{random:uniform(), E} || E <- L])]. + [E || {_, E} <- lists:keysort(1, [{rand:uniform(), E} || E <- L])]. -sync_0(suite) -> []; -sync_0(doc) -> - ["OTP-5770. sync/0."]; +%% OTP-5770. sync/0. sync_0(Config) when is_list(Config) -> Timeout = 180, ct:timetrap({seconds,Timeout}), init_high_level_trace(Timeout), - ?line init_condition(Config), + init_condition(Config), N_cps = - case ?t:os_type() of + case test_server:os_type() of {unix, Osname} when Osname =:= linux; Osname =:= openbsd; Osname =:= darwin -> @@ -2957,82 +2899,80 @@ sync_0(Config) when is_list(Config) -> Names = [lists:concat([cp,N]) || N <- lists:seq(1, N_cps)], Cps = start_and_sync(Names), - ?line wait_for_ready_net(Config), + wait_for_ready_net(Config), write_high_level_trace(Config), stop_nodes(Cps), - ?line init_condition(Config), + init_condition(Config), ok. start_and_sync([]) -> []; start_and_sync([Name | Names]) -> - ?line {ok, N} = start_node(Name, slave, []), - ?line {Time, _Void} = rpc:call(N, timer, tc, [global, sync, []]), - ?t:format("~p: ~p~n", [Name, Time]), + {ok, N} = start_node(Name, slave, []), + {Time, _Void} = rpc:call(N, timer, tc, [global, sync, []]), + io:format("~p: ~p~n", [Name, Time]), [N | start_and_sync(Names)]. %%%----------------------------------------------------------------- %%% Testing of change of global_groups parameter. %%%----------------------------------------------------------------- -global_groups_change(suite) -> []; -global_groups_change(doc) -> ["Test change of global_groups parameter."]; +%% Test change of global_groups parameter. global_groups_change(Config) -> Timeout = 90, ct:timetrap({seconds,Timeout}), init_high_level_trace(Timeout), - ?line init_condition(Config), - ?line M = from($@, atom_to_list(node())), - - % Create the .app files and the boot script - ?line {KernelVer, StdlibVer} = create_script_dc("dc"), - ?line case is_real_system(KernelVer, StdlibVer) of - true -> - Options = []; - false -> - Options = [local] - end, + init_condition(Config), + M = from($@, atom_to_list(node())), + + %% Create the .app files and the boot script + {KernelVer, StdlibVer} = create_script_dc("dc"), + case is_real_system(KernelVer, StdlibVer) of + true -> + Options = []; + false -> + Options = [local] + end, + + ok = systools:make_script("dc", Options), - ?line ok = systools:make_script("dc", Options), - [Ncp1,Ncp2,Ncp3,Ncp4,Ncp5,NcpA,NcpB,NcpC,NcpD,NcpE] = node_names([cp1,cp2,cp3,cp4,cp5,cpA,cpB,cpC,cpD,cpE], Config), - % Write config files - ?line Dir = ?config(priv_dir,Config), - ?line {ok, Fd_dc} = file:open(filename:join(Dir, "sys.config"), [write]), - ?line config_dc1(Fd_dc, Ncp1, Ncp2, Ncp3, NcpA, NcpB, NcpC, NcpD, NcpE), - ?line file:close(Fd_dc), - ?line Config1 = filename:join(Dir, "sys"), - - % Test [cp1, cp2, cp3] - ?line {ok, Cp1} = start_node_boot(Ncp1, Config1, dc), - ?line {ok, Cp2} = start_node_boot(Ncp2, Config1, dc), - ?line {ok, Cp3} = start_node_boot(Ncp3, Config1, dc), - ?line {ok, CpA} = start_node_boot(NcpA, Config1, dc), - ?line {ok, CpB} = start_node_boot(NcpB, Config1, dc), - ?line {ok, CpC} = start_node_boot(NcpC, Config1, dc), - ?line {ok, CpD} = start_node_boot(NcpD, Config1, dc), - ?line {ok, CpE} = start_node_boot(NcpE, Config1, dc), - - ?line pong = rpc:call(Cp1, net_adm, ping, [Cp2]), - ?line pong = rpc:call(Cp1, net_adm, ping, [Cp3]), - ?line pang = rpc:call(Cp1, net_adm, ping, - [list_to_atom(lists:concat(["cp5@", M]))]), - ?line pong = rpc:call(Cp2, net_adm, ping, [Cp3]), - ?line pang = rpc:call(Cp2, net_adm, ping, - [list_to_atom(lists:concat(["cp5@", M]))]), - - ?line {TestGG4, yes} = rpc:call(CpB, ?MODULE, start_proc, [test]), - ?line {TestGG5, yes} = rpc:call(CpE, ?MODULE, start_proc, [test]), - - - ?line pong = rpc:call(CpA, net_adm, ping, [CpC]), - ?line pong = rpc:call(CpC, net_adm, ping, [CpB]), - ?line pong = rpc:call(CpD, net_adm, ping, [CpC]), - ?line pong = rpc:call(CpE, net_adm, ping, [CpD]), - - ?line + %% Write config files + Dir = proplists:get_value(priv_dir,Config), + {ok, Fd_dc} = file:open(filename:join(Dir, "sys.config"), [write]), + config_dc1(Fd_dc, Ncp1, Ncp2, Ncp3, NcpA, NcpB, NcpC, NcpD, NcpE), + file:close(Fd_dc), + Config1 = filename:join(Dir, "sys"), + + %% Test [cp1, cp2, cp3] + {ok, Cp1} = start_node_boot(Ncp1, Config1, dc), + {ok, Cp2} = start_node_boot(Ncp2, Config1, dc), + {ok, Cp3} = start_node_boot(Ncp3, Config1, dc), + {ok, CpA} = start_node_boot(NcpA, Config1, dc), + {ok, CpB} = start_node_boot(NcpB, Config1, dc), + {ok, CpC} = start_node_boot(NcpC, Config1, dc), + {ok, CpD} = start_node_boot(NcpD, Config1, dc), + {ok, CpE} = start_node_boot(NcpE, Config1, dc), + + pong = rpc:call(Cp1, net_adm, ping, [Cp2]), + pong = rpc:call(Cp1, net_adm, ping, [Cp3]), + pang = rpc:call(Cp1, net_adm, ping, + [list_to_atom(lists:concat(["cp5@", M]))]), + pong = rpc:call(Cp2, net_adm, ping, [Cp3]), + pang = rpc:call(Cp2, net_adm, ping, + [list_to_atom(lists:concat(["cp5@", M]))]), + + {TestGG4, yes} = rpc:call(CpB, ?MODULE, start_proc, [test]), + {TestGG5, yes} = rpc:call(CpE, ?MODULE, start_proc, [test]), + + + pong = rpc:call(CpA, net_adm, ping, [CpC]), + pong = rpc:call(CpC, net_adm, ping, [CpB]), + pong = rpc:call(CpD, net_adm, ping, [CpC]), + pong = rpc:call(CpE, net_adm, ping, [CpD]), + ?UNTIL(begin TestGG4_1 = rpc:call(CpA, global, whereis_name, [test]), TestGG4_2 = rpc:call(CpB, global, whereis_name, [test]), @@ -3049,88 +2989,87 @@ global_groups_change(Config) -> (TestGG5_2 =:= TestGG5) end), - ?line ?t:format( "#### nodes() ~p~n",[nodes()]), + io:format( "#### nodes() ~p~n",[nodes()]), + + XDcWa1 = rpc:call(Cp1, global_group, info, []), + XDcWa2 = rpc:call(Cp2, global_group, info, []), + XDcWa3 = rpc:call(Cp3, global_group, info, []), + io:format( "#### XDcWa1 ~p~n",[XDcWa1]), + io:format( "#### XDcWa2 ~p~n",[XDcWa2]), + io:format( "#### XDcWa3 ~p~n",[XDcWa3]), - ?line XDcWa1 = rpc:call(Cp1, global_group, info, []), - ?line XDcWa2 = rpc:call(Cp2, global_group, info, []), - ?line XDcWa3 = rpc:call(Cp3, global_group, info, []), - ?line ?t:format( "#### XDcWa1 ~p~n",[XDcWa1]), - ?line ?t:format( "#### XDcWa2 ~p~n",[XDcWa2]), - ?line ?t:format( "#### XDcWa3 ~p~n",[XDcWa3]), + stop_node(CpC), - ?line stop_node(CpC), - %% Read the current configuration parameters, and change them - ?line OldEnv = + OldEnv = rpc:call(Cp1, application_controller, prep_config_change, []), - ?line {value, {kernel, OldKernel}} = lists:keysearch(kernel, 1, OldEnv), + {value, {kernel, OldKernel}} = lists:keysearch(kernel, 1, OldEnv), - ?line GG1 = + GG1 = lists:sort([mk_node(Ncp1, M), mk_node(Ncp2, M), mk_node(Ncp5, M)]), - ?line GG2 = lists:sort([mk_node(Ncp3, M)]), - ?line GG3 = lists:sort([mk_node(Ncp4, M)]), - ?line GG4 = lists:sort([mk_node(NcpA, M), mk_node(NcpB, M)]), - ?line GG5 = + GG2 = lists:sort([mk_node(Ncp3, M)]), + GG3 = lists:sort([mk_node(Ncp4, M)]), + GG4 = lists:sort([mk_node(NcpA, M), mk_node(NcpB, M)]), + GG5 = lists:sort([mk_node(NcpC, M), mk_node(NcpD, M), mk_node(NcpE, M)]), - ?line NewNG = {global_groups,[{gg1, normal, GG1}, - {gg2, normal, GG2}, - {gg3, normal, GG3}, - {gg4, normal, GG4}, - {gg5, hidden, GG5}]}, - - ?line NewKernel = + NewNG = {global_groups,[{gg1, normal, GG1}, + {gg2, normal, GG2}, + {gg3, normal, GG3}, + {gg4, normal, GG4}, + {gg5, hidden, GG5}]}, + + NewKernel = [{kernel, lists:keyreplace(global_groups, 1, OldKernel, NewNG)}], - ?line ok = rpc:call(Cp1, application_controller, test_change_apps, - [[kernel], [NewKernel]]), - ?line ok = rpc:call(Cp2, application_controller, test_change_apps, - [[kernel], [NewKernel]]), - ?line ok = rpc:call(Cp3, application_controller, test_change_apps, - [[kernel], [NewKernel]]), - ?line ok = rpc:call(CpA, application_controller, test_change_apps, - [[kernel], [NewKernel]]), - ?line ok = rpc:call(CpB, application_controller, test_change_apps, - [[kernel], [NewKernel]]), - ?line ok = rpc:call(CpD, application_controller, test_change_apps, - [[kernel], [NewKernel]]), - ?line ok = rpc:call(CpE, application_controller, test_change_apps, - [[kernel], [NewKernel]]), - - ?line ?t:format("#### ~p~n",[multicall]), - ?line ?t:format( "#### ~p~n",[multicall]), + ok = rpc:call(Cp1, application_controller, test_change_apps, + [[kernel], [NewKernel]]), + ok = rpc:call(Cp2, application_controller, test_change_apps, + [[kernel], [NewKernel]]), + ok = rpc:call(Cp3, application_controller, test_change_apps, + [[kernel], [NewKernel]]), + ok = rpc:call(CpA, application_controller, test_change_apps, + [[kernel], [NewKernel]]), + ok = rpc:call(CpB, application_controller, test_change_apps, + [[kernel], [NewKernel]]), + ok = rpc:call(CpD, application_controller, test_change_apps, + [[kernel], [NewKernel]]), + ok = rpc:call(CpE, application_controller, test_change_apps, + [[kernel], [NewKernel]]), + + io:format("#### ~p~n",[multicall]), + io:format( "#### ~p~n",[multicall]), %% no idea to check the result from the rpc because the other %% nodes will disconnect test server, and thus the result will %% always be {badrpc, nodedown} - ?line rpc:multicall([Cp1, Cp2, Cp3, CpA, CpB, CpD, CpE], - application_controller, config_change, [OldEnv]), + rpc:multicall([Cp1, Cp2, Cp3, CpA, CpB, CpD, CpE], + application_controller, config_change, [OldEnv]), - ?line {ok, Fd_dc2} = file:open(filename:join(Dir, "sys2.config"), [write]), - ?line config_dc2(Fd_dc2, NewNG, Ncp1, Ncp2, Ncp3), - ?line file:close(Fd_dc2), - ?line Config2 = filename:join(Dir, "sys2"), - ?line {ok, CpC} = start_node_boot(NcpC, Config2, dc), + {ok, Fd_dc2} = file:open(filename:join(Dir, "sys2.config"), [write]), + config_dc2(Fd_dc2, NewNG, Ncp1, Ncp2, Ncp3), + file:close(Fd_dc2), + Config2 = filename:join(Dir, "sys2"), + {ok, CpC} = start_node_boot(NcpC, Config2, dc), - ?line sync_and_wait(CpA), - ?line sync_and_wait(CpD), - - ?line pong = rpc:call(CpA, net_adm, ping, [CpC]), - ?line pong = rpc:call(CpC, net_adm, ping, [CpB]), - ?line pong = rpc:call(CpD, net_adm, ping, [CpC]), - ?line pong = rpc:call(CpE, net_adm, ping, [CpD]), - - ?line GG5 = + sync_and_wait(CpA), + sync_and_wait(CpD), + + pong = rpc:call(CpA, net_adm, ping, [CpC]), + pong = rpc:call(CpC, net_adm, ping, [CpB]), + pong = rpc:call(CpD, net_adm, ping, [CpC]), + pong = rpc:call(CpE, net_adm, ping, [CpD]), + + GG5 = lists:sort([mk_node(NcpC, M)|rpc:call(CpC, erlang, nodes, [])]), - ?line GG5 = + GG5 = lists:sort([mk_node(NcpD, M)|rpc:call(CpD, erlang, nodes, [])]), - ?line GG5 = + GG5 = lists:sort([mk_node(NcpE, M)|rpc:call(CpE, erlang, nodes, [])]), - ?line false = + false = lists:member(mk_node(NcpC, M), rpc:call(CpA, erlang, nodes, [])), - ?line false = + false = lists:member(mk_node(NcpC, M), rpc:call(CpB, erlang, nodes, [])), - ?line ?UNTIL(begin TestGG4a = rpc:call(CpA, global, whereis_name, [test]), TestGG4b = rpc:call(CpB, global, whereis_name, [test]), @@ -3147,171 +3086,171 @@ global_groups_change(Config) -> (TestGG5 =:= TestGG5e) end), - ?line Info1 = rpc:call(Cp1, global_group, info, []), - ?line Info2 = rpc:call(Cp2, global_group, info, []), - ?line Info3 = rpc:call(Cp3, global_group, info, []), - ?line InfoA = rpc:call(CpA, global_group, info, []), - ?line InfoB = rpc:call(CpB, global_group, info, []), - ?line InfoC = rpc:call(CpC, global_group, info, []), - ?line InfoD = rpc:call(CpD, global_group, info, []), - ?line InfoE = rpc:call(CpE, global_group, info, []), - ?line ?t:format( "#### Info1 ~p~n",[Info1]), - ?line ?t:format( "#### Info2 ~p~n",[Info2]), - ?line ?t:format( "#### Info3 ~p~n",[Info3]), - ?line ?t:format( "#### InfoA ~p~n",[InfoA]), - ?line ?t:format( "#### InfoB ~p~n",[InfoB]), - ?line ?t:format( "#### InfoC ~p~n",[InfoC]), - ?line ?t:format( "#### InfoD ~p~n",[InfoD]), - ?line ?t:format( "#### InfoE ~p~n",[InfoE]), - - ?line {global_groups, GGNodes} = NewNG, - - ?line Info1ok = [{state, synced}, - {own_group_name, gg1}, - {own_group_nodes, GG1}, - {synced_nodes, [mk_node(Ncp2, M)]}, - {sync_error, []}, - {no_contact, [mk_node(Ncp5, M)]}, - {other_groups, remove_gg_pub_type(lists:keydelete - (gg1, 1, GGNodes))}, - {monitoring, []}], - - - ?line Info2ok = [{state, synced}, - {own_group_name, gg1}, - {own_group_nodes, GG1}, - {synced_nodes, [mk_node(Ncp1, M)]}, - {sync_error, []}, - {no_contact, [mk_node(Ncp5, M)]}, - {other_groups, remove_gg_pub_type(lists:keydelete - (gg1, 1, GGNodes))}, - {monitoring, []}], - - ?line Info3ok = [{state, synced}, - {own_group_name, gg2}, - {own_group_nodes, GG2}, - {synced_nodes, []}, - {sync_error, []}, - {no_contact, []}, - {other_groups, remove_gg_pub_type(lists:keydelete - (gg2, 1, GGNodes))}, - {monitoring, []}], - - ?line InfoAok = [{state, synced}, - {own_group_name, gg4}, - {own_group_nodes, GG4}, - {synced_nodes, lists:delete(mk_node(NcpA, M), GG4)}, - {sync_error, []}, - {no_contact, []}, - {other_groups, remove_gg_pub_type(lists:keydelete - (gg4, 1, GGNodes))}, - {monitoring, []}], - - ?line InfoBok = [{state, synced}, - {own_group_name, gg4}, - {own_group_nodes, GG4}, - {synced_nodes, lists:delete(mk_node(NcpB, M), GG4)}, - {sync_error, []}, - {no_contact, []}, - {other_groups, remove_gg_pub_type(lists:keydelete - (gg4, 1, GGNodes))}, - {monitoring, []}], - - ?line InfoCok = [{state, synced}, - {own_group_name, gg5}, - {own_group_nodes, GG5}, - {synced_nodes, lists:delete(mk_node(NcpC, M), GG5)}, - {sync_error, []}, - {no_contact, []}, - {other_groups, remove_gg_pub_type(lists:keydelete - (gg5, 1, GGNodes))}, - {monitoring, []}], - - ?line InfoDok = [{state, synced}, - {own_group_name, gg5}, - {own_group_nodes, GG5}, - {synced_nodes, lists:delete(mk_node(NcpD, M), GG5)}, - {sync_error, []}, - {no_contact, []}, - {other_groups, remove_gg_pub_type(lists:keydelete - (gg5, 1, GGNodes))}, - {monitoring, []}], - - ?line InfoEok = [{state, synced}, - {own_group_name, gg5}, - {own_group_nodes, GG5}, - {synced_nodes, lists:delete(mk_node(NcpE, M), GG5)}, - {sync_error, []}, - {no_contact, []}, - {other_groups, remove_gg_pub_type(lists:keydelete - (gg5, 1, GGNodes))}, - {monitoring, []}], - - - ?line case Info1 of - Info1ok -> - ok; - _ -> - test_server:fail({{"could not change the global groups" - " in node", Cp1}, {Info1, Info1ok}}) - end, + Info1 = rpc:call(Cp1, global_group, info, []), + Info2 = rpc:call(Cp2, global_group, info, []), + Info3 = rpc:call(Cp3, global_group, info, []), + InfoA = rpc:call(CpA, global_group, info, []), + InfoB = rpc:call(CpB, global_group, info, []), + InfoC = rpc:call(CpC, global_group, info, []), + InfoD = rpc:call(CpD, global_group, info, []), + InfoE = rpc:call(CpE, global_group, info, []), + io:format( "#### Info1 ~p~n",[Info1]), + io:format( "#### Info2 ~p~n",[Info2]), + io:format( "#### Info3 ~p~n",[Info3]), + io:format( "#### InfoA ~p~n",[InfoA]), + io:format( "#### InfoB ~p~n",[InfoB]), + io:format( "#### InfoC ~p~n",[InfoC]), + io:format( "#### InfoD ~p~n",[InfoD]), + io:format( "#### InfoE ~p~n",[InfoE]), + + {global_groups, GGNodes} = NewNG, + + Info1ok = [{state, synced}, + {own_group_name, gg1}, + {own_group_nodes, GG1}, + {synced_nodes, [mk_node(Ncp2, M)]}, + {sync_error, []}, + {no_contact, [mk_node(Ncp5, M)]}, + {other_groups, remove_gg_pub_type(lists:keydelete + (gg1, 1, GGNodes))}, + {monitoring, []}], + + + Info2ok = [{state, synced}, + {own_group_name, gg1}, + {own_group_nodes, GG1}, + {synced_nodes, [mk_node(Ncp1, M)]}, + {sync_error, []}, + {no_contact, [mk_node(Ncp5, M)]}, + {other_groups, remove_gg_pub_type(lists:keydelete + (gg1, 1, GGNodes))}, + {monitoring, []}], + + Info3ok = [{state, synced}, + {own_group_name, gg2}, + {own_group_nodes, GG2}, + {synced_nodes, []}, + {sync_error, []}, + {no_contact, []}, + {other_groups, remove_gg_pub_type(lists:keydelete + (gg2, 1, GGNodes))}, + {monitoring, []}], + + InfoAok = [{state, synced}, + {own_group_name, gg4}, + {own_group_nodes, GG4}, + {synced_nodes, lists:delete(mk_node(NcpA, M), GG4)}, + {sync_error, []}, + {no_contact, []}, + {other_groups, remove_gg_pub_type(lists:keydelete + (gg4, 1, GGNodes))}, + {monitoring, []}], + + InfoBok = [{state, synced}, + {own_group_name, gg4}, + {own_group_nodes, GG4}, + {synced_nodes, lists:delete(mk_node(NcpB, M), GG4)}, + {sync_error, []}, + {no_contact, []}, + {other_groups, remove_gg_pub_type(lists:keydelete + (gg4, 1, GGNodes))}, + {monitoring, []}], + + InfoCok = [{state, synced}, + {own_group_name, gg5}, + {own_group_nodes, GG5}, + {synced_nodes, lists:delete(mk_node(NcpC, M), GG5)}, + {sync_error, []}, + {no_contact, []}, + {other_groups, remove_gg_pub_type(lists:keydelete + (gg5, 1, GGNodes))}, + {monitoring, []}], + + InfoDok = [{state, synced}, + {own_group_name, gg5}, + {own_group_nodes, GG5}, + {synced_nodes, lists:delete(mk_node(NcpD, M), GG5)}, + {sync_error, []}, + {no_contact, []}, + {other_groups, remove_gg_pub_type(lists:keydelete + (gg5, 1, GGNodes))}, + {monitoring, []}], + + InfoEok = [{state, synced}, + {own_group_name, gg5}, + {own_group_nodes, GG5}, + {synced_nodes, lists:delete(mk_node(NcpE, M), GG5)}, + {sync_error, []}, + {no_contact, []}, + {other_groups, remove_gg_pub_type(lists:keydelete + (gg5, 1, GGNodes))}, + {monitoring, []}], + + + case Info1 of + Info1ok -> + ok; + _ -> + ct:fail({{"could not change the global groups" + " in node", Cp1}, {Info1, Info1ok}}) + end, - ?line case Info2 of - Info2ok -> - ok; - _ -> - test_server:fail({{"could not change the global groups" - " in node", Cp2}, {Info2, Info2ok}}) - end, + case Info2 of + Info2ok -> + ok; + _ -> + ct:fail({{"could not change the global groups" + " in node", Cp2}, {Info2, Info2ok}}) + end, - ?line case Info3 of - Info3ok -> - ok; - _ -> - test_server:fail({{"could not change the global groups" - " in node", Cp3}, {Info3, Info3ok}}) - end, + case Info3 of + Info3ok -> + ok; + _ -> + ct:fail({{"could not change the global groups" + " in node", Cp3}, {Info3, Info3ok}}) + end, - ?line case InfoA of - InfoAok -> - ok; - _ -> - test_server:fail({{"could not change the global groups" - " in node", CpA}, {InfoA, InfoAok}}) - end, + case InfoA of + InfoAok -> + ok; + _ -> + ct:fail({{"could not change the global groups" + " in node", CpA}, {InfoA, InfoAok}}) + end, - ?line case InfoB of - InfoBok -> - ok; - _ -> - test_server:fail({{"could not change the global groups" - " in node", CpB}, {InfoB, InfoBok}}) - end, + case InfoB of + InfoBok -> + ok; + _ -> + ct:fail({{"could not change the global groups" + " in node", CpB}, {InfoB, InfoBok}}) + end, - ?line case InfoC of - InfoCok -> - ok; - _ -> - test_server:fail({{"could not change the global groups" - " in node", CpC}, {InfoC, InfoCok}}) - end, + case InfoC of + InfoCok -> + ok; + _ -> + ct:fail({{"could not change the global groups" + " in node", CpC}, {InfoC, InfoCok}}) + end, - ?line case InfoD of - InfoDok -> - ok; - _ -> - test_server:fail({{"could not change the global groups" - " in node", CpD}, {InfoD, InfoDok}}) - end, + case InfoD of + InfoDok -> + ok; + _ -> + ct:fail({{"could not change the global groups" + " in node", CpD}, {InfoD, InfoDok}}) + end, - ?line case InfoE of - InfoEok -> - ok; - _ -> - test_server:fail({{"could not change the global groups" - " in node", CpE}, {InfoE, InfoEok}}) - end, + case InfoE of + InfoEok -> + ok; + _ -> + ct:fail({{"could not change the global groups" + " in node", CpE}, {InfoE, InfoEok}}) + end, write_high_level_trace(Config), % no good since CpC was restarted stop_node(Cp1), @@ -3323,7 +3262,7 @@ global_groups_change(Config) -> stop_node(CpD), stop_node(CpE), - ?line init_condition(Config), + init_condition(Config), ok. sync_and_wait(Node) -> @@ -3353,43 +3292,43 @@ sync_and_wait(Node) -> is_real_system(KernelVsn, StdlibVsn) -> LibDir = code:lib_dir(), filelib:is_dir(filename:join(LibDir, "kernel-" ++ KernelVsn)) - andalso - filelib:is_dir(filename:join(LibDir, "stdlib-" ++ StdlibVsn)). + andalso + filelib:is_dir(filename:join(LibDir, "stdlib-" ++ StdlibVsn)). create_script_dc(ScriptName) -> - ?line Name = filename:join(".", ScriptName), - ?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 {_, Version} = init:script_id(), - ?line io:format(Fd, - "{release, {\"Test release 3\", \"~s\"}, \n" - " {erts, \"4.4\"}, \n" - " [{kernel, \"~s\"}, {stdlib, \"~s\"}]}.\n", - [Version, KernelVer, StdlibVer]), - ?line file:close(Fd), + Name = filename:join(".", ScriptName), + Apps = application_controller:which_applications(), + {value,{_,_,KernelVer}} = lists:keysearch(kernel,1,Apps), + {value,{_,_,StdlibVer}} = lists:keysearch(stdlib,1,Apps), + {ok,Fd} = file:open(Name ++ ".rel", [write]), + {_, Version} = init:script_id(), + io:format(Fd, + "{release, {\"Test release 3\", \"~s\"}, \n" + " {erts, \"4.4\"}, \n" + " [{kernel, \"~s\"}, {stdlib, \"~s\"}]}.\n", + [Version, KernelVer, StdlibVer]), + file:close(Fd), {KernelVer, StdlibVer}. %% Not used? config_dc(Fd, Ncp1, Ncp2, Ncp3) -> M = from($@, atom_to_list(node())), io:format(Fd, "[{kernel, [{sync_nodes_optional, ['~s@~s','~s@~s','~s@~s']}," - "{sync_nodes_timeout, 1000}," - "{global_groups, [{gg1, ['~s@~s', '~s@~s']}," - " {gg2, ['~s@~s']}]}" - " ]}].~n", + "{sync_nodes_timeout, 1000}," + "{global_groups, [{gg1, ['~s@~s', '~s@~s']}," + " {gg2, ['~s@~s']}]}" + " ]}].~n", [Ncp1, M, Ncp2, M, Ncp3, M, Ncp1, M, Ncp2, M, Ncp3, M]). config_dc1(Fd, Ncp1, Ncp2, Ncp3, NcpA, NcpB, NcpC, NcpD, NcpE) -> M = from($@, atom_to_list(node())), io:format(Fd, "[{kernel, [{sync_nodes_optional, ['~s@~s','~s@~s','~s@~s','~s@~s','~s@~s','~s@~s','~s@~s','~s@~s']}," - "{sync_nodes_timeout, 1000}," - "{global_groups, [{gg1, ['~s@~s', '~s@~s']}," - " {gg2, ['~s@~s']}," - " {gg4, normal, ['~s@~s','~s@~s','~s@~s']}," - " {gg5, hidden, ['~s@~s','~s@~s']}]}]}].~n", + "{sync_nodes_timeout, 1000}," + "{global_groups, [{gg1, ['~s@~s', '~s@~s']}," + " {gg2, ['~s@~s']}," + " {gg4, normal, ['~s@~s','~s@~s','~s@~s']}," + " {gg5, hidden, ['~s@~s','~s@~s']}]}]}].~n", [Ncp1, M, Ncp2, M, Ncp3, M, NcpA, M, NcpB, M, NcpC, M, NcpD, M, NcpE, M, Ncp1, M, Ncp2, M, @@ -3400,8 +3339,8 @@ config_dc1(Fd, Ncp1, Ncp2, Ncp3, NcpA, NcpB, NcpC, NcpD, NcpE) -> config_dc2(Fd, NewGG, Ncp1, Ncp2, Ncp3) -> M = from($@, atom_to_list(node())), io:format(Fd, "[{kernel, [{sync_nodes_optional, ['~s@~s','~s@~s','~s@~s']}," - "{sync_nodes_timeout, 1000}," - "~p]}].~n", + "{sync_nodes_timeout, 1000}," + "~p]}].~n", [Ncp1, M, Ncp2, M, Ncp3, M, NewGG]). @@ -3413,33 +3352,33 @@ from(_H, []) -> []. other(A, [A, _B]) -> A; other(_, [_A, B]) -> B. - + %% this one runs at cp2 part1(Config, Main, Cp1, Cp3) -> case catch begin make_partition(Config, [Main, Cp1], [node(), Cp3]), - ?line {_Pid, yes} = start_proc(test2), - ?line {_Pid2, yes} = start_proc(test4) + {_Pid, yes} = start_proc(test2), + {_Pid2, yes} = start_proc(test4) end of {_, yes} -> ok; % w("ok", []); {'EXIT', _R} -> ok - % w("global_SUITE line:~w: ~p", [?LINE, _R]) + %% w("global_SUITE line:~w: ~p", [?LINE, _R]) end. %% Runs at Cp2 part1_5(Config, Main, Cp1, Cp3) -> case catch begin make_partition(Config, [Main, Cp1], [node(), Cp3]), - ?line {_Pid1, yes} = start_proc_basic(name12), - ?line {_Pid2, yes} = + {_Pid1, yes} = start_proc_basic(name12), + {_Pid2, yes} = rpc:call(Cp3, ?MODULE, start_proc_basic, [name03]) end of {_, yes} -> ok; % w("ok", []); {'EXIT', _R} -> ok - % w("global_SUITE line:~w: ~p", [?LINE, _R]) + %% w("global_SUITE line:~w: ~p", [?LINE, _R]) end. w(X,Y) -> @@ -3450,7 +3389,7 @@ w(X,Y) -> %% this one runs on one node in Part2 %% The partition is ready when is_ready_partition(Config) returns (true). make_partition(Config, Part1, Part2) -> - Dir = ?config(priv_dir, Config), + Dir = proplists:get_value(priv_dir, Config), Ns = [begin Name = lists:concat([atom_to_list(N),"_",msec(),".part"]), File = filename:join([Dir, Name]), @@ -3502,7 +3441,7 @@ is_ready_partition(Config) -> true. make_partition_file(Config) -> - Dir = ?config(priv_dir, Config), + Dir = proplists:get_value(priv_dir, Config), filename:join([Dir, atom_to_list(make_partition_done)]). %% this one runs at cp3 @@ -3513,37 +3452,36 @@ part2(Config, Parent, Main, Cp0, Cp1, Cp2, Cp3, Cp4, Cp5, Cp6) -> part3(Config, Parent, Main, Cp0, Cp1, Cp2, Cp3, Cp4, Cp5, Cp6) -> make_partition(Config, [Main, Cp0, Cp1, Cp2], [Cp3, Cp4, Cp5, Cp6]), start_procs(Parent, Cp4, Cp5, Cp6, Config), - % Make Cp6 alone - ?line rpc_cast(Cp5, ?MODULE, crash, [12000]), - ?line rpc_cast(Cp6, ?MODULE, alone, [Cp0, Cp3]). + %% Make Cp6 alone + rpc_cast(Cp5, ?MODULE, crash, [12000]), + rpc_cast(Cp6, ?MODULE, alone, [Cp0, Cp3]). start_procs(Parent, N1, N2, N3, Config) -> S1 = lists:sort([N1, N2, N3]), - ?line ?UNTIL(begin NN = lists:sort(nodes()), S1 =:= NN end), - ?line Pid3 = start_proc3(test1), - ?line Pid4 = rpc:call(N1, ?MODULE, start_proc3, [test2]), - ?line assert_pid(Pid4), - ?line Pid5 = rpc:call(N2, ?MODULE, start_proc3, [test3]), - ?line assert_pid(Pid5), - ?line Pid6 = rpc:call(N3, ?MODULE, start_proc3, [test4]), - ?line assert_pid(Pid6), - ?line yes = global:register_name(test1, Pid3), - ?line yes = global:register_name(test2, Pid4, {global, notify_all_name}), - ?line yes = global:register_name(test3, Pid5, {global, random_notify_name}), + Pid3 = start_proc3(test1), + Pid4 = rpc:call(N1, ?MODULE, start_proc3, [test2]), + assert_pid(Pid4), + Pid5 = rpc:call(N2, ?MODULE, start_proc3, [test3]), + assert_pid(Pid5), + Pid6 = rpc:call(N3, ?MODULE, start_proc3, [test4]), + assert_pid(Pid6), + yes = global:register_name(test1, Pid3), + yes = global:register_name(test2, Pid4, fun global:notify_all_name/3), + yes = global:register_name(test3, Pid5, fun global:random_notify_name/3), Resolve = fun(Name, Pid1, Pid2) -> Parent ! {resolve_called, Name, node()}, {Min, Max} = minmax(Pid1, Pid2), exit(Min, kill), Max end, - ?line yes = global:register_name(test4, Pid6, Resolve). + yes = global:register_name(test4, Pid6, Resolve). + - collect_resolves() -> cr(0). cr(Res) -> receive @@ -3573,7 +3511,7 @@ start_proc() -> receive Pid -> Pid end. - + start_proc(Name) -> Pid = spawn(?MODULE, p_init, [self(), Name]), @@ -3608,7 +3546,7 @@ start_proc_basic(Name) -> end. init_proc_basic(Parent, Name) -> - X = global:register_name(Name, self(), {?MODULE, fix_basic_name}), + X = global:register_name(Name, self(), fun ?MODULE:fix_basic_name/3), Parent ! {self(),X}, loop(). @@ -3617,7 +3555,7 @@ single_node(Time, Node, Config) -> lists:foreach(fun(N) -> _ = erlang:disconnect_node(N) end, nodes()), ?UNTIL(get_known(node()) =:= [node()]), spawn(?MODULE, init_2, []), - test_server:sleep(Time - msec()), + sleep(Time - msec()), net_adm:ping(Node). init_2() -> @@ -3629,12 +3567,12 @@ loop_2() -> receive die -> ok end. - + msec() -> msec(now()). msec(T) -> - element(1,T)*1000000000 + element(2,T)*1000 + element(3,T) div 1000. + element(1,T)*1000000000 + element(2,T)*1000 + element(3,T) div 1000. assert_pid(Pid) -> if @@ -3679,13 +3617,15 @@ sreq(Pid, Msg) -> alone(N1, N2) -> lists:foreach(fun(Node) -> true = erlang:disconnect_node(Node) end, nodes()), - test_server:sleep(12000), + ct:sleep(12000), net_adm:ping(N1), net_adm:ping(N2), yes = global:register_name(test5, self()). crash(Time) -> - test_server:sleep(Time), + %% ct:sleep/1 will not work because it calls a server process + %% that does not run on other nodes. + timer:sleep(Time), erlang:halt(). loop() -> @@ -3747,23 +3687,23 @@ pr_diff(Str, T0, T1) -> {_, {H,M,S}} = calendar:time_difference(T0, T1), ((H*60+M)*60)+S end, - test_server:format(1,"~13s: ~w (diff: ~w)",[Str, T1, Diff]), + ct:pal(?HI_VERBOSITY,"~13s: ~w (diff: ~w)",[Str, T1, Diff]), if Diff > 100 -> - test_server:format(1,"~s: ** LARGE DIFF ~w~n", [Str, Diff]); + io:format(1,"~s: ** LARGE DIFF ~w~n", [Str, Diff]); true -> ok end. -endif. now_diff({A1,B1,C1},{A2,B2,C2}) -> - C1-C2 + 1000000*((B1-B2) + 1000000*(A1-A2)). + C1-C2 + 1000000*((B1-B2) + 1000000*(A1-A2)). start_node_boot(Name, Config, Boot) -> Pa = filename:dirname(code:which(?MODULE)), Res = test_server:start_node(Name, peer, [{args, " -pa " ++ Pa ++ - " -config " ++ Config ++ - " -boot " ++ atom_to_list(Boot)}]), + " -config " ++ Config ++ + " -boot " ++ atom_to_list(Boot)}]), record_started_node(Res). %% Increase the timeout for when an upcoming connection is teared down @@ -3789,13 +3729,13 @@ start_node(Name0, How, Args, Config) -> Pa = filename:dirname(code:which(?MODULE)), R = test_server:start_node(Name, How, [{args, Args ++ " " ++ - "-kernel net_setuptime 100 " -% "-noshell " + "-kernel net_setuptime 100 " + %% "-noshell " "-pa " ++ Pa}, {linked, false} -]), + ]), %% {linked,false} only seems to work for slave nodes. -% test_server:sleep(1000), + %% ct:sleep(1000), record_started_node(R). start_node_rel(Name0, Rel, Config) -> @@ -3806,14 +3746,14 @@ start_node_rel(Name0, Rel, Config) -> Rel when is_atom(Rel) -> {[{release, atom_to_list(Rel)}], ""}; RelList -> - {RelList, ""} - end, + {RelList, ""} + end, Env = [], Pa = filename:dirname(code:which(?MODULE)), Res = test_server:start_node(Name, peer, [{args, Compat ++ - " -kernel net_setuptime 100 " + " -kernel net_setuptime 100 " " -pa " ++ Pa}, {erl, Release}] ++ Env), record_started_node(Res). @@ -3843,57 +3783,45 @@ stop_nodes(Nodes) -> lists:foreach(fun(Node) -> stop_node(Node) end, Nodes). stop_node(Node) -> - ?line ?t:stop_node(Node). + test_server:stop_node(Node). stop() -> lists:foreach(fun(Node) -> - ?t:stop_node(Node) + test_server:stop_node(Node) end, nodes()). -dbg_logs(Name) -> dbg_logs(Name, ?NODES). - -dbg_logs(Name, Nodes) -> - lists:foreach(fun(N) -> - F = lists:concat([Name, ".log.", N, ".txt"]), - ?line ok = sys:log_to_file({global_name_server, N}, F) - end, Nodes). - - -global_lost_nodes(suite) -> - []; -global_lost_nodes(doc) -> - ["Tests that locally loaded nodes do not loose contact with other nodes."]; +%% Tests that locally loaded nodes do not loose contact with other nodes. global_lost_nodes(Config) when is_list(Config) -> Timeout = 60, ct:timetrap({seconds,Timeout}), init_high_level_trace(Timeout), - ?line init_condition(Config), - - ?line {ok, Node1} = start_node(node1, Config), - ?line {ok, Node2} = start_node(node2, Config), + init_condition(Config), + + {ok, Node1} = start_node(node1, Config), + {ok, Node2} = start_node(node2, Config), - ?line wait_for_ready_net(Config), + wait_for_ready_net(Config), - ?line io:format("Nodes: ~p", [nodes()]), - ?line io:format("Nodes at node1: ~p", - [rpc:call(Node1, erlang, nodes, [])]), - ?line io:format("Nodes at node2: ~p", - [rpc:call(Node2, erlang, nodes, [])]), + io:format("Nodes: ~p", [nodes()]), + io:format("Nodes at node1: ~p", + [rpc:call(Node1, erlang, nodes, [])]), + io:format("Nodes at node2: ~p", + [rpc:call(Node2, erlang, nodes, [])]), - ?line rpc_cast(Node1, ?MODULE, global_load, [node_1,Node2,node_2]), - ?line rpc_cast(Node2, ?MODULE, global_load, [node_2,Node1,node_1]), + rpc_cast(Node1, ?MODULE, global_load, [node_1,Node2,node_2]), + rpc_cast(Node2, ?MODULE, global_load, [node_2,Node1,node_1]), lost_nodes_waiter(Node1, Node2), write_high_level_trace(Config), - ?line stop_node(Node1), - ?line stop_node(Node2), - ?line init_condition(Config), + stop_node(Node1), + stop_node(Node2), + init_condition(Config), ok. global_load(MyName, OtherNode, OtherName) -> - ?line yes = global:register_name(MyName, self()), + yes = global:register_name(MyName, self()), io:format("Registered ~p",[MyName]), global_load1(OtherNode, OtherName, 0). @@ -3901,32 +3829,32 @@ global_load1(_OtherNode, _OtherName, 2) -> io:format("*** ~p giving up. No use.", [node()]), init:stop(); global_load1(OtherNode, OtherName, Fails) -> - test_server:sleep(1000), - ?line case catch global:whereis_name(OtherName) of - Pid when is_pid(Pid) -> - io:format("~p says: ~p is still there.", - [node(),OtherName]), - global_load1(OtherNode, OtherName, Fails); - Other -> - io:format("~p says: ~p is lost (~p) Pinging.", - [ node(), OtherName, Other]), - case net_adm:ping(OtherNode) of - pong -> - io:format("Re-established contact to ~p", - [OtherName]); - pang -> - io:format("PANIC! Other node is DEAD.", []), - init:stop() - end, - global_load1(OtherNode, OtherName, Fails+1) - end. + ct:sleep(1000), + case catch global:whereis_name(OtherName) of + Pid when is_pid(Pid) -> + io:format("~p says: ~p is still there.", + [node(),OtherName]), + global_load1(OtherNode, OtherName, Fails); + Other -> + io:format("~p says: ~p is lost (~p) Pinging.", + [ node(), OtherName, Other]), + case net_adm:ping(OtherNode) of + pong -> + io:format("Re-established contact to ~p", + [OtherName]); + pang -> + io:format("PANIC! Other node is DEAD.", []), + init:stop() + end, + global_load1(OtherNode, OtherName, Fails+1) + end. lost_nodes_waiter(N1, N2) -> - ?line net_kernel:monitor_nodes(true), + net_kernel:monitor_nodes(true), receive {nodedown, Node} when Node =:= N1 ; Node =:= N2 -> io:format("~p went down!",[Node]), - ?line ?t:fail("Node went down.") + ct:fail("Node went down.") after 10000 -> ok end, @@ -3934,36 +3862,33 @@ lost_nodes_waiter(N1, N2) -> -mass_death(suite) -> - []; -mass_death(doc) -> - ["Tests the simultaneous death of many processes with registered names"]; +%% Tests the simultaneous death of many processes with registered names. mass_death(Config) when is_list(Config) -> Timeout = 90, ct:timetrap({seconds,Timeout}), init_high_level_trace(Timeout), - ?line init_condition(Config), - ?line OrigNames = global:registered_names(), + init_condition(Config), + OrigNames = global:registered_names(), %% Start nodes - ?line Cps = [cp1,cp2,cp3,cp4,cp5], - ?line Nodes = [begin {ok, Node} = start_node(Cp, Config), Node end || - Cp <- Cps], - ?line io:format("Nodes: ~p~n", [Nodes]), - ?line Ns = lists:seq(1, 40), + Cps = [cp1,cp2,cp3,cp4,cp5], + Nodes = [begin {ok, Node} = start_node(Cp, Config), Node end || + Cp <- Cps], + io:format("Nodes: ~p~n", [Nodes]), + Ns = lists:seq(1, 40), %% Start processes with globally registered names on the nodes - ?line {Pids,[]} = rpc:multicall(Nodes, ?MODULE, mass_spawn, [Ns]), - ?line io:format("Pids: ~p~n", [Pids]), + {Pids,[]} = rpc:multicall(Nodes, ?MODULE, mass_spawn, [Ns]), + io:format("Pids: ~p~n", [Pids]), %% Wait... - ?line test_server:sleep(10000), + ct:sleep(10000), %% Check the globally registered names - ?line NewNames = global:registered_names(), - ?line io:format("NewNames: ~p~n", [NewNames]), - ?line Ndiff = lists:sort(NewNames--OrigNames), - ?line io:format("Ndiff: ~p~n", [Ndiff]), - ?line Ndiff = lists:sort(mass_names(Nodes, Ns)), + NewNames = global:registered_names(), + io:format("NewNames: ~p~n", [NewNames]), + Ndiff = lists:sort(NewNames--OrigNames), + io:format("Ndiff: ~p~n", [Ndiff]), + Ndiff = lists:sort(mass_names(Nodes, Ns)), %% %% Kill the root pids - ?line lists:foreach(fun (Pid) -> Pid ! drop_dead end, Pids), + lists:foreach(fun (Pid) -> Pid ! drop_dead end, Pids), %% Start probing and wait for all registered names to disappear {YYYY,MM,DD} = date(), {H,M,S} = time(), @@ -3972,22 +3897,21 @@ mass_death(Config) when is_list(Config) -> wait_mass_death(Nodes, OrigNames, erlang:now(), Config). wait_mass_death(Nodes, OrigNames, Then, Config) -> - ?line Names = global:registered_names(), - ?line - case Names--OrigNames of - [] -> - ?line T = now_diff(erlang:now(), Then) div 1000, - ?line lists:foreach( - fun (Node) -> - stop_node(Node) - end, Nodes), - ?line init_condition(Config), - {comment,lists:flatten(io_lib:format("~.3f s~n", [T/1000.0]))}; - Ndiff -> - ?line io:format("Ndiff: ~p~n", [Ndiff]), - ?line test_server:sleep(1000), - ?line wait_mass_death(Nodes, OrigNames, Then, Config) - end. + Names = global:registered_names(), + case Names--OrigNames of + [] -> + T = now_diff(erlang:now(), Then) div 1000, + lists:foreach( + fun (Node) -> + stop_node(Node) + end, Nodes), + init_condition(Config), + {comment,lists:flatten(io_lib:format("~.3f s~n", [T/1000.0]))}; + Ndiff -> + io:format("Ndiff: ~p~n", [Ndiff]), + ct:sleep(1000), + wait_mass_death(Nodes, OrigNames, Then, Config) + end. mass_spawn([]) -> ok; @@ -4017,7 +3941,7 @@ mass_name(Node, N) -> start_nodes(L, How, Config) -> start_nodes2(L, How, 0, Config), Nodes = collect_nodes(0, length(L)), - ?line ?UNTIL([] =:= Nodes -- nodes()), + ?UNTIL([] =:= Nodes -- nodes()), put(?nodes_tag, Nodes), %% Pinging doesn't help, we have to wait too, for nodes() to become %% correct on the other node. @@ -4041,7 +3965,7 @@ verify_nodes(Nodes, Config) -> verify_nodes([], _N, _Config) -> []; verify_nodes([Node | Rest], N, Config) -> - ?line ?UNTIL( + ?UNTIL( case rpc:call(Node, erlang, nodes, []) of Nodes when is_list(Nodes) -> case N =:= lists:sort([Node | Nodes]) of @@ -4073,7 +3997,7 @@ start_nodes2([Name | Rest], How, N, Config) -> Self ! {N, R}, %% sleeping is necessary, or with peer nodes, they will %% go down again, despite {linked, false}. - test_server:sleep(100000) + ct:sleep(100000) end), start_nodes2(Rest, How, N+1, Config). @@ -4085,13 +4009,6 @@ collect_nodes(N, Max) -> [Node | collect_nodes(N+1, Max)] end. -only_element(_E, []) -> - true; -only_element(E, [E|R]) -> - only_element(E, R); -only_element(_E, _) -> - false. - exit_p(Pid) -> Ref = erlang:monitor(process, Pid), Pid ! die, @@ -4114,6 +4031,11 @@ wait_for_exit_fast(Pid) -> ok end. +sleep(Time) when Time > 0 -> + ct:sleep(Time); +sleep(_Time) -> + ok. + check_everywhere(Nodes, Name, Config) -> ?UNTIL(begin case rpc:multicall(Nodes, global, whereis_name, [Name]) of @@ -4154,14 +4076,12 @@ remove_gg_pub_type([{GG, _, Nodes}|Rest]) -> %% Better do this in a slave node. %% (The transition from links to monitors does not affect this case.) -garbage_messages(suite) -> - []; garbage_messages(Config) when is_list(Config) -> Timeout = 25, ct:timetrap({seconds,Timeout}), init_high_level_trace(Timeout), - ?line init_condition(Config), - ?line [Slave] = start_nodes([garbage_messages], slave, Config), + init_condition(Config), + [Slave] = start_nodes([garbage_messages], slave, Config), Fun = fun() -> {links,L} = process_info(whereis(global_name_server), links), lists:foreach(fun(Pid) -> Pid ! {garbage,to,you} end, L), @@ -4169,15 +4089,15 @@ garbage_messages(Config) when is_list(Config) -> _Any -> ok end end, - ?line Pid = spawn_link(Slave, erlang, apply, [Fun,[]]), - ?t:sleep(2000), - ?line Global = rpc:call(Slave, erlang, whereis, [global_name_server]), - ?line {registered_name,global_name_server} = + Pid = spawn_link(Slave, erlang, apply, [Fun,[]]), + ct:sleep(2000), + Global = rpc:call(Slave, erlang, whereis, [global_name_server]), + {registered_name,global_name_server} = rpc:call(Slave, erlang, process_info, [Global,registered_name]), - ?line true = unlink(Pid), + true = unlink(Pid), write_high_level_trace(Config), - ?line stop_node(Slave), - ?line init_condition(Config), + stop_node(Slave), + init_condition(Config), ok. wait_for_ready_net(Config) -> @@ -4185,13 +4105,13 @@ wait_for_ready_net(Config) -> wait_for_ready_net(Nodes0, Config) -> Nodes = lists:sort(Nodes0), - ?t:format("wait_for_ready_net ~p~n", [Nodes]), + io:format("wait_for_ready_net ~p~n", [Nodes]), ?UNTIL(begin lists:all(fun(N) -> Nodes =:= get_known(N) end, Nodes) and - lists:all(fun(N) -> - LNs = rpc:call(N, erlang, nodes, []), - Nodes =:= lists:sort([N | LNs]) - end, Nodes) + lists:all(fun(N) -> + LNs = rpc:call(N, erlang, nodes, []), + Nodes =:= lists:sort([N | LNs]) + end, Nodes) end). get_known(Node) -> @@ -4206,7 +4126,7 @@ quite_a_few_nodes(Max) -> N = try ulimit("ulimit -u") catch _:_ -> - ulimit("ulimit -p") % can fail... + ulimit("ulimit -p") % can fail... end, lists:min([(N - 40) div 3, Max]). @@ -4240,10 +4160,10 @@ rpc_cast(Node, Module, Function, Args, File) -> %% The emulator now ensures that the node has been removed from %% nodes(). -rpc_disconnect_node(Node, DisconnectedNode, _Config) -> - True = rpc:call(Node, erlang, disconnect_node, [DisconnectedNode]), - False = lists:member(DisconnectedNode, rpc:call(Node, erlang, nodes, [])), - {true, false} = {True, False}. +rpc_disconnect_node(Node, DisconnectedNode, Config) -> + true = rpc:call(Node, erlang, disconnect_node, [DisconnectedNode]), + ?UNTIL + (not lists:member(DisconnectedNode, rpc:call(Node, erlang, nodes, []))). %%% %%% Utility @@ -4263,15 +4183,15 @@ start_tracer() -> Pid = spawn(fun() -> tracer([]) end), case catch register(my_tracer, Pid) of {'EXIT', _} -> - ?t:fail(re_register_my_tracer); + ct:fail(re_register_my_tracer); _ -> ok end. tracer(L) -> receive - % {save, Term} -> - % tracer([{now(),Term} | L]); + %% {save, Term} -> + %% tracer([{now(),Term} | L]); {get, From} -> From ! {trace, lists:reverse(L)}, tracer([]); @@ -4304,7 +4224,7 @@ collect_tracers(Nodes) -> trace_message(M) -> case catch my_tracer ! M of {'EXIT', _} -> - ?t:fail(my_tracer_not_registered); + ct:fail(my_tracer_not_registered); _ -> ok end. diff --git a/lib/kernel/test/global_SUITE_data/global_trace.erl b/lib/kernel/test/global_SUITE_data/global_trace.erl index 1396d86c79..b4af4ed76e 100644 --- a/lib/kernel/test/global_SUITE_data/global_trace.erl +++ b/lib/kernel/test/global_SUITE_data/global_trace.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2013. All Rights Reserved. +%% Copyright Ericsson AB 2007-2016. 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/lib/kernel/test/global_group_SUITE.erl b/lib/kernel/test/global_group_SUITE.erl index 799b0d9d05..594ee6b537 100644 --- a/lib/kernel/test/global_group_SUITE.erl +++ b/lib/kernel/test/global_group_SUITE.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2011. All Rights Reserved. +%% Copyright Ericsson AB 1998-2016. 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/. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at %% -%% 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. +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% @@ -27,15 +28,17 @@ -export([init_per_testcase/2, end_per_testcase/2]). -%-compile(export_all). +%%-compile(export_all). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -define(NODES, [node()|nodes()]). -define(UNTIL(Seq), loop_until_true(fun() -> Seq end)). -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap,{minutes,5}}]. all() -> [start_gg_proc, no_gg_proc, no_gg_proc_sync, compatible, @@ -45,10 +48,10 @@ groups() -> []. init_per_group(_GroupName, Config) -> - Config. + Config. end_per_group(_GroupName, Config) -> - Config. + Config. init_per_suite(Config) -> @@ -76,15 +79,13 @@ end_per_suite(_Config) -> ok. -define(TESTCASE, testcase_name). --define(testcase, ?config(?TESTCASE, Config)). +-define(testcase, proplists:get_value(?TESTCASE, Config)). -init_per_testcase(Case, Config) when is_atom(Case), is_list(Config) -> - Dog=?t:timetrap(?t:minutes(5)), - [{?TESTCASE, Case}, {watchdog, Dog}|Config]. +init_per_testcase(Case, Config) -> + Config. -end_per_testcase(_Func, Config) -> - Dog=?config(watchdog, Config), - ?t:timetrap_cancel(Dog). +end_per_testcase(_Func, _Config) -> + ok. %%----------------------------------------------------------------- %% Test suites for global groups. @@ -93,198 +94,191 @@ end_per_testcase(_Func, Config) -> %%----------------------------------------------------------------- -start_gg_proc(suite) -> []; -start_gg_proc(doc) -> ["Check that the global_group processes are started automatically. "]; +%% Check that the global_group processes are started automatically. . start_gg_proc(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(120)), - - ?line Dir = ?config(priv_dir, Config), - ?line File = filename:join(Dir, "global_group.config"), - ?line {ok, Fd}=file:open(File, [write]), + Dir = proplists:get_value(priv_dir, Config), + File = filename:join(Dir, "global_group.config"), + {ok, Fd}=file:open(File, [write]), [Ncp1,Ncp2,Ncp3] = node_names([cp1, cp2, cp3], Config), - ?line config(Fd, Ncp1, Ncp2, Ncp3, "cpx", "cpy", "cpz", "cpq"), + config(Fd, Ncp1, Ncp2, Ncp3, "cpx", "cpy", "cpz", "cpq"), - ?line Cp1nn = node_at(Ncp1), - ?line Cp2nn = node_at(Ncp2), - ?line Cp3nn = node_at(Ncp3), + Cp1nn = node_at(Ncp1), + Cp2nn = node_at(Ncp2), + Cp3nn = node_at(Ncp3), - ?line {ok, Cp1} = start_node(Ncp1, Config), - ?line {ok, Cp2} = start_node(Ncp2, Config), - ?line {ok, Cp3} = start_node(Ncp3, Config), + {ok, Cp1} = start_node(Ncp1, Config), + {ok, Cp2} = start_node(Ncp2, Config), + {ok, Cp3} = start_node(Ncp3, Config), - ?line [] = rpc:call(Cp1, global_group, registered_names, [{node, Cp1nn}]), - ?line [] = rpc:call(Cp2, global_group, registered_names, [{node, Cp2nn}]), - ?line [] = rpc:call(Cp3, global_group, registered_names, [{node, Cp3nn}]), + [] = rpc:call(Cp1, global_group, registered_names, [{node, Cp1nn}]), + [] = rpc:call(Cp2, global_group, registered_names, [{node, Cp2nn}]), + [] = rpc:call(Cp3, global_group, registered_names, [{node, Cp3nn}]), - % stop the nodes, and make sure names are released. + %% stop the nodes, and make sure names are released. stop_node(Cp1), stop_node(Cp2), stop_node(Cp3), - ?line ?UNTIL(undefined =:= global:whereis_name(test)), - ?line test_server:timetrap_cancel(Dog), + ?UNTIL(undefined =:= global:whereis_name(test)), ok. - -no_gg_proc(suite) -> []; -no_gg_proc(doc) -> ["Start a system without global groups. Nodes are not " - "synced at start (sync_nodes_optional is not defined)"]; + +%% Start a system without global groups. Nodes are not +%% synced at start (sync_nodes_optional is not defined). no_gg_proc(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(200)), - - ?line Dir = ?config(priv_dir, Config), - ?line File = filename:join(Dir, "no_global_group.config"), - ?line {ok, Fd} = file:open(File, [write]), - ?line config_no(Fd), - - ?line NN = node_name(atom_to_list(node())), - ?line Cp1nn = list_to_atom("cp1@" ++ NN), - ?line Cp2nn = list_to_atom("cp2@" ++ NN), - ?line Cp3nn = list_to_atom("cp3@" ++ NN), - ?line Cpxnn = list_to_atom("cpx@" ++ NN), - ?line Cpynn = list_to_atom("cpy@" ++ NN), - ?line Cpznn = list_to_atom("cpz@" ++ NN), - - ?line {ok, Cp1} = start_node_no(cp1, Config), - ?line {ok, Cp2} = start_node_no(cp2, Config), - ?line {ok, Cp3} = start_node_no(cp3, Config), - ?line {ok, Cpx} = start_node_no(cpx, Config), - ?line {ok, Cpy} = start_node_no(cpy, Config), - ?line {ok, Cpz} = start_node_no(cpz, Config), + Dir = proplists:get_value(priv_dir, Config), + File = filename:join(Dir, "no_global_group.config"), + {ok, Fd} = file:open(File, [write]), + config_no(Fd), + + NN = node_name(atom_to_list(node())), + Cp1nn = list_to_atom("cp1@" ++ NN), + Cp2nn = list_to_atom("cp2@" ++ NN), + Cp3nn = list_to_atom("cp3@" ++ NN), + Cpxnn = list_to_atom("cpx@" ++ NN), + Cpynn = list_to_atom("cpy@" ++ NN), + Cpznn = list_to_atom("cpz@" ++ NN), + + {ok, Cp1} = start_node_no(cp1, Config), + {ok, Cp2} = start_node_no(cp2, Config), + {ok, Cp3} = start_node_no(cp3, Config), + {ok, Cpx} = start_node_no(cpx, Config), + {ok, Cpy} = start_node_no(cpy, Config), + {ok, Cpz} = start_node_no(cpz, Config), %% let the nodes know of each other - ?line pong = rpc:call(Cp1, net_adm, ping, [Cp2nn]), - ?line pong = rpc:call(Cp2, net_adm, ping, [Cp3nn]), - ?line pong = rpc:call(Cp3, net_adm, ping, [Cpxnn]), - ?line pong = rpc:call(Cpx, net_adm, ping, [Cpynn]), - ?line pong = rpc:call(Cpy, net_adm, ping, [Cpznn]), + pong = rpc:call(Cp1, net_adm, ping, [Cp2nn]), + pong = rpc:call(Cp2, net_adm, ping, [Cp3nn]), + pong = rpc:call(Cp3, net_adm, ping, [Cpxnn]), + pong = rpc:call(Cpx, net_adm, ping, [Cpynn]), + pong = rpc:call(Cpy, net_adm, ping, [Cpznn]), - ?line wait_for_ready_net(), + wait_for_ready_net(), - ?line [test_server] = rpc:call(Cp1, global_group, registered_names, [{node, Cp1nn}]), - ?line [test_server] = rpc:call(Cp2, global_group, registered_names, [{node, Cp2nn}]), - ?line [test_server] = rpc:call(Cp3, global_group, registered_names, [{node, Cp3nn}]), - ?line [test_server] = rpc:call(Cp1, global_group, registered_names, [{node, Cpxnn}]), - ?line [test_server] = rpc:call(Cp2, global_group, registered_names, [{node, Cpynn}]), - ?line [test_server] = rpc:call(Cp3, global_group, registered_names, [{node, Cpznn}]), + [test_server] = rpc:call(Cp1, global_group, registered_names, [{node, Cp1nn}]), + [test_server] = rpc:call(Cp2, global_group, registered_names, [{node, Cp2nn}]), + [test_server] = rpc:call(Cp3, global_group, registered_names, [{node, Cp3nn}]), + [test_server] = rpc:call(Cp1, global_group, registered_names, [{node, Cpxnn}]), + [test_server] = rpc:call(Cp2, global_group, registered_names, [{node, Cpynn}]), + [test_server] = rpc:call(Cp3, global_group, registered_names, [{node, Cpznn}]), - % start a proc and register it - ?line {Pid2, yes} = rpc:call(Cp2, ?MODULE, start_proc, [test2]), + %% start a proc and register it + {Pid2, yes} = rpc:call(Cp2, ?MODULE, start_proc, [test2]), - ?line RegNames = lists:sort([test2,test_server]), + RegNames = lists:sort([test2,test_server]), - ?line RegNames = + RegNames = lists:sort( rpc:call(Cp1, global_group, registered_names, [{node, Cp1nn}])), - ?line RegNames = + RegNames = lists:sort( rpc:call(Cp2, global_group, registered_names, [{node, Cp2nn}])), - ?line RegNames = + RegNames = lists:sort( rpc:call(Cp3, global_group, registered_names, [{node, Cp3nn}])), - ?line RegNames = + RegNames = lists:sort( rpc:call(Cp1, global_group, registered_names, [{node, Cpxnn}])), - ?line RegNames = + RegNames = lists:sort( rpc:call(Cp2, global_group, registered_names, [{node, Cpynn}])), - ?line RegNames = + RegNames = lists:sort( rpc:call(Cp3, global_group, registered_names, [{node, Cpznn}])), - ?line undefined = rpc:call(Cp3, global_group, global_groups, []), + undefined = rpc:call(Cp3, global_group, global_groups, []), - ?line Own_nodes_should = [node(), Cp1nn, Cp2nn, Cp3nn, - Cpxnn, Cpynn, Cpznn], - ?line Own_nodes = rpc:call(Cp3, global_group, own_nodes, []), - ?line [] = (Own_nodes -- Own_nodes_should), - ?line [] = (Own_nodes_should -- Own_nodes), - - ?line Pid2 = rpc:call(Cp1, global_group, send, [test2, {ping, self()}]), - ?line receive - {pong, Cp2} -> ok - after - 2000 -> test_server:fail(timeout2) - end, - ?line Pid2 = rpc:call(Cp2, global_group, send, [test2, {ping, self()}]), - ?line receive - {pong, Cp2} -> ok - after - 2000 -> test_server:fail(timeout3) - end, - ?line Pid2 = rpc:call(Cpz, global_group, send, [test2, {ping, self()}]), - ?line receive - {pong, Cp2} -> ok - after - 2000 -> test_server:fail(timeout4) - end, - - - % start a proc and register it - ?line {PidX, yes} = rpc:call(Cpx, ?MODULE, start_proc, [test]), + Own_nodes_should = [node(), Cp1nn, Cp2nn, Cp3nn, + Cpxnn, Cpynn, Cpznn], + Own_nodes = rpc:call(Cp3, global_group, own_nodes, []), + [] = (Own_nodes -- Own_nodes_should), + [] = (Own_nodes_should -- Own_nodes), + + Pid2 = rpc:call(Cp1, global_group, send, [test2, {ping, self()}]), + receive + {pong, Cp2} -> ok + after + 2000 -> ct:fail(timeout2) + end, + Pid2 = rpc:call(Cp2, global_group, send, [test2, {ping, self()}]), + receive + {pong, Cp2} -> ok + after + 2000 -> ct:fail(timeout3) + end, + Pid2 = rpc:call(Cpz, global_group, send, [test2, {ping, self()}]), + receive + {pong, Cp2} -> ok + after + 2000 -> ct:fail(timeout4) + end, + + + %% start a proc and register it + {PidX, yes} = rpc:call(Cpx, ?MODULE, start_proc, [test]), %%------------------------------------ %% Test monitor nodes %%------------------------------------ - ?line Pid2 = rpc:call(Cp1, global_group, send, [{node, Cp2nn}, test2, monitor]), - ?line PidX = rpc:call(Cpx, global_group, send, [{node, Cpxnn}, test, monitor]), + Pid2 = rpc:call(Cp1, global_group, send, [{node, Cp2nn}, test2, monitor]), + PidX = rpc:call(Cpx, global_group, send, [{node, Cpxnn}, test, monitor]), - % Kill node Cp1 - ?line Pid2 = + %% Kill node Cp1 + Pid2 = rpc:call(Cp2, global_group, send, [{node, Cp2nn}, test2, {wait_nodedown, Cp1}]), - ?line PidX = + PidX = rpc:call(Cpx, global_group, send, [{node, Cpxnn}, test, {wait_nodedown, Cp1}]), - ?line test_server:sleep(100), - ?line stop_node(Cp1), - ?line test_server:sleep(1000), - - ?line ok = assert_loop(Cp2, Cp2nn, test2, Pid2, loop), - ?line ok = assert_loop(Cpx, Cpxnn, test, PidX, loop), + ct:sleep(100), + stop_node(Cp1), + ct:sleep(1000), - % Kill node Cpz - ?line Pid2 = + ok = assert_loop(Cp2, Cp2nn, test2, Pid2, loop), + ok = assert_loop(Cpx, Cpxnn, test, PidX, loop), + + %% Kill node Cpz + Pid2 = rpc:call(Cp2, global_group, send, [{node, Cp2nn}, test2, {wait_nodedown, Cpz}]), - ?line PidX = + PidX = rpc:call(Cpx, global_group, send, [{node, Cpxnn}, test, {wait_nodedown, Cpz}]), - ?line test_server:sleep(100), - ?line stop_node(Cpz), - ?line test_server:sleep(1000), - - ?line ok = assert_loop(Cp2, Cp2nn, test2, Pid2, loop), - ?line ok = assert_loop(Cpx, Cpxnn, test, PidX, loop), + ct:sleep(100), + stop_node(Cpz), + ct:sleep(1000), + + ok = assert_loop(Cp2, Cp2nn, test2, Pid2, loop), + ok = assert_loop(Cpx, Cpxnn, test, PidX, loop), - % Restart node Cp1 - ?line Pid2 = + %% Restart node Cp1 + Pid2 = rpc:call(Cp2, global_group, send, [{node, Cp2nn}, test2, {wait_nodeup, Cp1}]), - ?line PidX = + PidX = rpc:call(Cpx, global_group, send, [{node, Cpxnn}, test, {wait_nodeup, Cp1}]), - ?line {ok, Cp1} = start_node_no(cp1, Config), - ?line pong = rpc:call(Cp2, net_adm, ping, [Cp1nn]), - ?line pong = rpc:call(Cpx, net_adm, ping, [Cp1nn]), - ?line wait_for_ready_net(), - - ?line ok = assert_loop(Cp2, Cp2nn, test2, Pid2, loop), - ?line ok = assert_loop(Cpx, Cpxnn, test, PidX, loop), + {ok, Cp1} = start_node_no(cp1, Config), + pong = rpc:call(Cp2, net_adm, ping, [Cp1nn]), + pong = rpc:call(Cpx, net_adm, ping, [Cp1nn]), + wait_for_ready_net(), + + ok = assert_loop(Cp2, Cp2nn, test2, Pid2, loop), + ok = assert_loop(Cpx, Cpxnn, test, PidX, loop), - % Restart node Cpz - ?line Pid2 = + %% Restart node Cpz + Pid2 = rpc:call(Cp2, global_group, send, [{node, Cp2nn}, test2, {wait_nodeup, Cpz}]), - ?line PidX = + PidX = rpc:call(Cpx, global_group, send, [{node, Cpxnn}, test, {wait_nodeup, Cpz}]), - ?line {ok, Cpz} = start_node_no(cpz, Config), - ?line pong = rpc:call(Cp2, net_adm, ping, [Cpznn]), - ?line pong = rpc:call(Cpx, net_adm, ping, [Cpznn]), - ?line wait_for_ready_net(), - - ?line ok = assert_loop(Cp2, Cp2nn, test2, Pid2, loop), - ?line ok = assert_loop(Cpx, Cpxnn, test, PidX, loop), + {ok, Cpz} = start_node_no(cpz, Config), + pong = rpc:call(Cp2, net_adm, ping, [Cpznn]), + pong = rpc:call(Cpx, net_adm, ping, [Cpznn]), + wait_for_ready_net(), + + ok = assert_loop(Cp2, Cp2nn, test2, Pid2, loop), + ok = assert_loop(Cpx, Cpxnn, test, PidX, loop), - % stop the nodes, and make sure names are released. + %% stop the nodes, and make sure names are released. stop_node(Cp1), stop_node(Cp2), stop_node(Cp3), @@ -292,174 +286,169 @@ no_gg_proc(Config) when is_list(Config) -> stop_node(Cpy), stop_node(Cpz), - ?line ?UNTIL(undefined =:= global:whereis_name(test)), - ?line test_server:timetrap_cancel(Dog), + ?UNTIL(undefined =:= global:whereis_name(test)), ok. - -no_gg_proc_sync(suite) -> []; -no_gg_proc_sync(doc) -> - ["Start a system without global groups, but syncing the nodes by using " - "sync_nodes_optional."]; -no_gg_proc_sync(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(200)), - ?line Dir = ?config(priv_dir, Config), - ?line File = filename:join(Dir, "no_global_group_sync.config"), - ?line {ok, Fd} = file:open(File, [write]), +%% Start a system without global groups, but syncing the nodes by using +%% sync_nodes_optional. +no_gg_proc_sync(Config) when is_list(Config) -> + Dir = proplists:get_value(priv_dir, Config), + File = filename:join(Dir, "no_global_group_sync.config"), + {ok, Fd} = file:open(File, [write]), [Ncp1,Ncp2,Ncp3,Ncpx,Ncpy,Ncpz] = node_names([cp1,cp2,cp3,cpx,cpy,cpz], Config), - ?line config_sync(Fd, Ncp1, Ncp2, Ncp3, Ncpx, Ncpy, Ncpz), - - ?line Cp1nn = node_at(Ncp1), - ?line Cp2nn = node_at(Ncp2), - ?line Cp3nn = node_at(Ncp3), - ?line Cpxnn = node_at(Ncpx), - ?line Cpynn = node_at(Ncpy), - ?line Cpznn = node_at(Ncpz), - - ?line {ok, Cp1} = start_node_no2(Ncp1, Config), - ?line {ok, Cp2} = start_node_no2(Ncp2, Config), - ?line {ok, Cp3} = start_node_no2(Ncp3, Config), - ?line {ok, Cpx} = start_node_no2(Ncpx, Config), - ?line {ok, Cpy} = start_node_no2(Ncpy, Config), - ?line {ok, Cpz} = start_node_no2(Ncpz, Config), + config_sync(Fd, Ncp1, Ncp2, Ncp3, Ncpx, Ncpy, Ncpz), + + Cp1nn = node_at(Ncp1), + Cp2nn = node_at(Ncp2), + Cp3nn = node_at(Ncp3), + Cpxnn = node_at(Ncpx), + Cpynn = node_at(Ncpy), + Cpznn = node_at(Ncpz), + + {ok, Cp1} = start_node_no2(Ncp1, Config), + {ok, Cp2} = start_node_no2(Ncp2, Config), + {ok, Cp3} = start_node_no2(Ncp3, Config), + {ok, Cpx} = start_node_no2(Ncpx, Config), + {ok, Cpy} = start_node_no2(Ncpy, Config), + {ok, Cpz} = start_node_no2(Ncpz, Config), %% let the nodes know of each other - ?line pong = rpc:call(Cp1, net_adm, ping, [Cp2nn]), - ?line pong = rpc:call(Cp2, net_adm, ping, [Cp3nn]), - ?line pong = rpc:call(Cp3, net_adm, ping, [Cpxnn]), - ?line pong = rpc:call(Cpx, net_adm, ping, [Cpynn]), - ?line pong = rpc:call(Cpy, net_adm, ping, [Cpznn]), + pong = rpc:call(Cp1, net_adm, ping, [Cp2nn]), + pong = rpc:call(Cp2, net_adm, ping, [Cp3nn]), + pong = rpc:call(Cp3, net_adm, ping, [Cpxnn]), + pong = rpc:call(Cpx, net_adm, ping, [Cpynn]), + pong = rpc:call(Cpy, net_adm, ping, [Cpznn]), - ?line wait_for_ready_net(), + wait_for_ready_net(), - ?line [test_server] = rpc:call(Cp1, global_group, registered_names, [{node, Cp1nn}]), - ?line [test_server] = rpc:call(Cp2, global_group, registered_names, [{node, Cp2nn}]), - ?line [test_server] = rpc:call(Cp3, global_group, registered_names, [{node, Cp3nn}]), - ?line [test_server] = rpc:call(Cp1, global_group, registered_names, [{node, Cpxnn}]), - ?line [test_server] = rpc:call(Cp2, global_group, registered_names, [{node, Cpynn}]), - ?line [test_server] = rpc:call(Cp3, global_group, registered_names, [{node, Cpznn}]), + [test_server] = rpc:call(Cp1, global_group, registered_names, [{node, Cp1nn}]), + [test_server] = rpc:call(Cp2, global_group, registered_names, [{node, Cp2nn}]), + [test_server] = rpc:call(Cp3, global_group, registered_names, [{node, Cp3nn}]), + [test_server] = rpc:call(Cp1, global_group, registered_names, [{node, Cpxnn}]), + [test_server] = rpc:call(Cp2, global_group, registered_names, [{node, Cpynn}]), + [test_server] = rpc:call(Cp3, global_group, registered_names, [{node, Cpznn}]), - % start a proc and register it - ?line {Pid2, yes} = rpc:call(Cp2, ?MODULE, start_proc, [test2]), + %% start a proc and register it + {Pid2, yes} = rpc:call(Cp2, ?MODULE, start_proc, [test2]), - ?line RegNames = lists:sort([test2,test_server]), + RegNames = lists:sort([test2,test_server]), - ?line RegNames = + RegNames = lists:sort( rpc:call(Cp1, global_group, registered_names, [{node, Cp1nn}])), - ?line RegNames = + RegNames = lists:sort( rpc:call(Cp2, global_group, registered_names, [{node, Cp2nn}])), - ?line RegNames = + RegNames = lists:sort( rpc:call(Cp3, global_group, registered_names, [{node, Cp3nn}])), - ?line RegNames = + RegNames = lists:sort( rpc:call(Cp1, global_group, registered_names, [{node, Cpxnn}])), - ?line RegNames = + RegNames = lists:sort( rpc:call(Cp2, global_group, registered_names, [{node, Cpynn}])), - ?line RegNames = + RegNames = lists:sort( rpc:call(Cp3, global_group, registered_names, [{node, Cpznn}])), - ?line undefined = rpc:call(Cp3, global_group, global_groups, []), + undefined = rpc:call(Cp3, global_group, global_groups, []), + + Own_nodes_should = [node(), Cp1nn, Cp2nn, Cp3nn, + Cpxnn, Cpynn, Cpznn], + Own_nodes = rpc:call(Cp3, global_group, own_nodes, []), + [] = (Own_nodes -- Own_nodes_should), + [] = (Own_nodes_should -- Own_nodes), + + Pid2 = rpc:call(Cp1, global_group, send, [test2, {ping, self()}]), + receive + {pong, Cp2} -> ok + after + 2000 -> ct:fail(timeout2) + end, + Pid2 = rpc:call(Cp2, global_group, send, [test2, {ping, self()}]), + receive + {pong, Cp2} -> ok + after + 2000 -> ct:fail(timeout3) + end, + Pid2 = rpc:call(Cpz, global_group, send, [test2, {ping, self()}]), + receive + {pong, Cp2} -> ok + after + 2000 -> ct:fail(timeout4) + end, - ?line Own_nodes_should = [node(), Cp1nn, Cp2nn, Cp3nn, - Cpxnn, Cpynn, Cpznn], - ?line Own_nodes = rpc:call(Cp3, global_group, own_nodes, []), - ?line [] = (Own_nodes -- Own_nodes_should), - ?line [] = (Own_nodes_should -- Own_nodes), - - ?line Pid2 = rpc:call(Cp1, global_group, send, [test2, {ping, self()}]), - ?line receive - {pong, Cp2} -> ok - after - 2000 -> test_server:fail(timeout2) - end, - ?line Pid2 = rpc:call(Cp2, global_group, send, [test2, {ping, self()}]), - ?line receive - {pong, Cp2} -> ok - after - 2000 -> test_server:fail(timeout3) - end, - ?line Pid2 = rpc:call(Cpz, global_group, send, [test2, {ping, self()}]), - ?line receive - {pong, Cp2} -> ok - after - 2000 -> test_server:fail(timeout4) - end, - - - % start a proc and register it - ?line {PidX, yes} = rpc:call(Cpx, ?MODULE, start_proc, [test]), + + %% start a proc and register it + {PidX, yes} = rpc:call(Cpx, ?MODULE, start_proc, [test]), %%------------------------------------ %% Test monitor nodes %%------------------------------------ - ?line Pid2 = rpc:call(Cp1, global_group, send, [{node, Cp2nn}, test2, monitor]), - ?line PidX = rpc:call(Cpx, global_group, send, [{node, Cpxnn}, test, monitor]), + Pid2 = rpc:call(Cp1, global_group, send, [{node, Cp2nn}, test2, monitor]), + PidX = rpc:call(Cpx, global_group, send, [{node, Cpxnn}, test, monitor]), - % Kill node Cp1 - ?line Pid2 = + %% Kill node Cp1 + Pid2 = rpc:call(Cp2, global_group, send, [{node, Cp2nn}, test2, {wait_nodedown, Cp1}]), - ?line PidX = + PidX = rpc:call(Cpx, global_group, send, [{node, Cpxnn}, test, {wait_nodedown, Cp1}]), - ?line test_server:sleep(100), - ?line stop_node(Cp1), - ?line test_server:sleep(1000), - - ?line ok = assert_loop(Cp2, Cp2nn, test2, Pid2, loop), - ?line ok = assert_loop(Cpx, Cpxnn, test, PidX, loop), + ct:sleep(100), + stop_node(Cp1), + ct:sleep(1000), + + ok = assert_loop(Cp2, Cp2nn, test2, Pid2, loop), + ok = assert_loop(Cpx, Cpxnn, test, PidX, loop), - % Kill node Cpz - ?line Pid2 = + %% Kill node Cpz + Pid2 = rpc:call(Cp2, global_group, send, [{node, Cp2nn}, test2, {wait_nodedown, Cpz}]), - ?line PidX = + PidX = rpc:call(Cpx, global_group, send, [{node, Cpxnn}, test, {wait_nodedown, Cpz}]), - ?line test_server:sleep(100), - ?line stop_node(Cpz), - ?line test_server:sleep(1000), - - ?line ok = assert_loop(Cp2, Cp2nn, test2, Pid2, loop), - ?line ok = assert_loop(Cpx, Cpxnn, test, PidX, loop), + ct:sleep(100), + stop_node(Cpz), + ct:sleep(1000), - % Restart node Cp1 - ?line Pid2 = + ok = assert_loop(Cp2, Cp2nn, test2, Pid2, loop), + ok = assert_loop(Cpx, Cpxnn, test, PidX, loop), + + %% Restart node Cp1 + Pid2 = rpc:call(Cp2, global_group, send, [{node, Cp2nn}, test2, {wait_nodeup, Cp1}]), - ?line PidX = + PidX = rpc:call(Cpx, global_group, send, [{node, Cpxnn}, test, {wait_nodeup, Cp1}]), - ?line {ok, Cp1} = start_node_no2(Ncp1, Config), - ?line pong = rpc:call(Cp2, net_adm, ping, [Cp1nn]), - ?line pong = rpc:call(Cpx, net_adm, ping, [Cp1nn]), - ?line wait_for_ready_net(), - - ?line ok = assert_loop(Cp2, Cp2nn, test2, Pid2, loop), - ?line ok = assert_loop(Cpx, Cpxnn, test, PidX, loop), + {ok, Cp1} = start_node_no2(Ncp1, Config), + pong = rpc:call(Cp2, net_adm, ping, [Cp1nn]), + pong = rpc:call(Cpx, net_adm, ping, [Cp1nn]), + wait_for_ready_net(), - % Restart node Cpz - ?line Pid2 = + ok = assert_loop(Cp2, Cp2nn, test2, Pid2, loop), + ok = assert_loop(Cpx, Cpxnn, test, PidX, loop), + + %% Restart node Cpz + Pid2 = rpc:call(Cp2, global_group, send, [{node, Cp2nn}, test2, {wait_nodeup, Cpz}]), - ?line PidX = + PidX = rpc:call(Cpx, global_group, send, [{node, Cpxnn}, test, {wait_nodeup, Cpz}]), - ?line {ok, Cpz} = start_node_no2(Ncpz, Config), - ?line pong = rpc:call(Cp2, net_adm, ping, [Cpznn]), - ?line pong = rpc:call(Cpx, net_adm, ping, [Cpznn]), - ?line wait_for_ready_net(), - - ?line ok = assert_loop(Cp2, Cp2nn, test2, Pid2, loop), - ?line ok = assert_loop(Cpx, Cpxnn, test, PidX, loop), + {ok, Cpz} = start_node_no2(Ncpz, Config), + pong = rpc:call(Cp2, net_adm, ping, [Cpznn]), + pong = rpc:call(Cpx, net_adm, ping, [Cpznn]), + wait_for_ready_net(), - % stop the nodes, and make sure names are released. + ok = assert_loop(Cp2, Cp2nn, test2, Pid2, loop), + ok = assert_loop(Cpx, Cpxnn, test, PidX, loop), + + %% stop the nodes, and make sure names are released. stop_node(Cp1), stop_node(Cp2), stop_node(Cp3), @@ -467,173 +456,168 @@ no_gg_proc_sync(Config) when is_list(Config) -> stop_node(Cpy), stop_node(Cpz), - ?line ?UNTIL(undefined =:= global:whereis_name(test)), - ?line test_server:timetrap_cancel(Dog), + ?UNTIL(undefined =:= global:whereis_name(test)), ok. - -compatible(suite) -> []; -compatible(doc) -> - ["Check that a system without global groups is compatible with the old R4 system."]; -compatible(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(200)), - ?line Dir = ?config(priv_dir, Config), - ?line File = filename:join(Dir, "global_group_comp.config"), - ?line {ok, Fd} = file:open(File, [write]), +%% Check that a system without global groups is compatible with the old R4 system. +compatible(Config) when is_list(Config) -> + Dir = proplists:get_value(priv_dir, Config), + File = filename:join(Dir, "global_group_comp.config"), + {ok, Fd} = file:open(File, [write]), [Ncp1,Ncp2,Ncp3,Ncpx,Ncpy,Ncpz] = node_names([cp1,cp2,cp3,cpx,cpy,cpz], Config), - ?line config_comp(Fd, Ncp1, Ncp2, Ncp3, Ncpx, Ncpy, Ncpz), - - ?line Cp1nn = node_at(Ncp1), - ?line Cp2nn = node_at(Ncp2), - ?line Cp3nn = node_at(Ncp3), - ?line Cpxnn = node_at(Ncpx), - ?line Cpynn = node_at(Ncpy), - ?line Cpznn = node_at(Ncpz), - - ?line {ok, Cp1} = start_node_comp(Ncp1, Config), - ?line {ok, Cp2} = start_node_comp(Ncp2, Config), - ?line {ok, Cp3} = start_node_comp(Ncp3, Config), - ?line {ok, Cpx} = start_node_comp(Ncpx, Config), - ?line {ok, Cpy} = start_node_comp(Ncpy, Config), - ?line {ok, Cpz} = start_node_comp(Ncpz, Config), + config_comp(Fd, Ncp1, Ncp2, Ncp3, Ncpx, Ncpy, Ncpz), + + Cp1nn = node_at(Ncp1), + Cp2nn = node_at(Ncp2), + Cp3nn = node_at(Ncp3), + Cpxnn = node_at(Ncpx), + Cpynn = node_at(Ncpy), + Cpznn = node_at(Ncpz), + + {ok, Cp1} = start_node_comp(Ncp1, Config), + {ok, Cp2} = start_node_comp(Ncp2, Config), + {ok, Cp3} = start_node_comp(Ncp3, Config), + {ok, Cpx} = start_node_comp(Ncpx, Config), + {ok, Cpy} = start_node_comp(Ncpy, Config), + {ok, Cpz} = start_node_comp(Ncpz, Config), %% let the nodes know of each other - ?line pong = rpc:call(Cp1, net_adm, ping, [Cp2nn]), - ?line pong = rpc:call(Cp2, net_adm, ping, [Cp3nn]), - ?line pong = rpc:call(Cp3, net_adm, ping, [Cpxnn]), - ?line pong = rpc:call(Cpx, net_adm, ping, [Cpynn]), - ?line pong = rpc:call(Cpy, net_adm, ping, [Cpznn]), + pong = rpc:call(Cp1, net_adm, ping, [Cp2nn]), + pong = rpc:call(Cp2, net_adm, ping, [Cp3nn]), + pong = rpc:call(Cp3, net_adm, ping, [Cpxnn]), + pong = rpc:call(Cpx, net_adm, ping, [Cpynn]), + pong = rpc:call(Cpy, net_adm, ping, [Cpznn]), - ?line wait_for_ready_net(), + wait_for_ready_net(), - ?line [test_server] = rpc:call(Cp1, global_group, registered_names, [{node, Cp1nn}]), - ?line [test_server] = rpc:call(Cp2, global_group, registered_names, [{node, Cp2nn}]), - ?line [test_server] = rpc:call(Cp3, global_group, registered_names, [{node, Cp3nn}]), - ?line [test_server] = rpc:call(Cp1, global_group, registered_names, [{node, Cpxnn}]), - ?line [test_server] = rpc:call(Cp2, global_group, registered_names, [{node, Cpynn}]), - ?line [test_server] = rpc:call(Cp3, global_group, registered_names, [{node, Cpznn}]), + [test_server] = rpc:call(Cp1, global_group, registered_names, [{node, Cp1nn}]), + [test_server] = rpc:call(Cp2, global_group, registered_names, [{node, Cp2nn}]), + [test_server] = rpc:call(Cp3, global_group, registered_names, [{node, Cp3nn}]), + [test_server] = rpc:call(Cp1, global_group, registered_names, [{node, Cpxnn}]), + [test_server] = rpc:call(Cp2, global_group, registered_names, [{node, Cpynn}]), + [test_server] = rpc:call(Cp3, global_group, registered_names, [{node, Cpznn}]), - % start a proc and register it - ?line {Pid2, yes} = rpc:call(Cp2, ?MODULE, start_proc, [test2]), + %% start a proc and register it + {Pid2, yes} = rpc:call(Cp2, ?MODULE, start_proc, [test2]), - ?line RegNames = lists:sort([test2,test_server]), + RegNames = lists:sort([test2,test_server]), - ?line RegNames = + RegNames = lists:sort( rpc:call(Cp1, global_group, registered_names, [{node, Cp1nn}])), - ?line RegNames = + RegNames = lists:sort( rpc:call(Cp2, global_group, registered_names, [{node, Cp2nn}])), - ?line RegNames = + RegNames = lists:sort( rpc:call(Cp3, global_group, registered_names, [{node, Cp3nn}])), - ?line RegNames = + RegNames = lists:sort( rpc:call(Cp1, global_group, registered_names, [{node, Cpxnn}])), - ?line RegNames = + RegNames = lists:sort( rpc:call(Cp2, global_group, registered_names, [{node, Cpynn}])), - ?line RegNames = + RegNames = lists:sort( rpc:call(Cp3, global_group, registered_names, [{node, Cpznn}])), - ?line undefined = rpc:call(Cp3, global_group, global_groups, []), + undefined = rpc:call(Cp3, global_group, global_groups, []), - ?line Own_nodes_should = [node(), Cp1nn, Cp2nn, Cp3nn, - Cpxnn, Cpynn, Cpznn], - ?line Own_nodes = rpc:call(Cp3, global_group, own_nodes, []), - ?line [] = (Own_nodes -- Own_nodes_should), - ?line [] = (Own_nodes_should -- Own_nodes), - - ?line Pid2 = rpc:call(Cp1, global_group, send, [test2, {ping, self()}]), - ?line receive - {pong, Cp2} -> ok - after - 2000 -> test_server:fail(timeout2) - end, - ?line Pid2 = rpc:call(Cp2, global_group, send, [test2, {ping, self()}]), - ?line receive - {pong, Cp2} -> ok - after - 2000 -> test_server:fail(timeout3) - end, - ?line Pid2 = rpc:call(Cpz, global_group, send, [test2, {ping, self()}]), - ?line receive - {pong, Cp2} -> ok - after - 2000 -> test_server:fail(timeout4) - end, - - - % start a proc and register it - ?line {PidX, yes} = rpc:call(Cpx, ?MODULE, start_proc, [test]), + Own_nodes_should = [node(), Cp1nn, Cp2nn, Cp3nn, + Cpxnn, Cpynn, Cpznn], + Own_nodes = rpc:call(Cp3, global_group, own_nodes, []), + [] = (Own_nodes -- Own_nodes_should), + [] = (Own_nodes_should -- Own_nodes), + + Pid2 = rpc:call(Cp1, global_group, send, [test2, {ping, self()}]), + receive + {pong, Cp2} -> ok + after + 2000 -> ct:fail(timeout2) + end, + Pid2 = rpc:call(Cp2, global_group, send, [test2, {ping, self()}]), + receive + {pong, Cp2} -> ok + after + 2000 -> ct:fail(timeout3) + end, + Pid2 = rpc:call(Cpz, global_group, send, [test2, {ping, self()}]), + receive + {pong, Cp2} -> ok + after + 2000 -> ct:fail(timeout4) + end, + + + %% start a proc and register it + {PidX, yes} = rpc:call(Cpx, ?MODULE, start_proc, [test]), %%------------------------------------ %% Test monitor nodes %%------------------------------------ - ?line Pid2 = rpc:call(Cp1, global_group, send, [{node, Cp2nn}, test2, monitor]), - ?line PidX = rpc:call(Cpx, global_group, send, [{node, Cpxnn}, test, monitor]), + Pid2 = rpc:call(Cp1, global_group, send, [{node, Cp2nn}, test2, monitor]), + PidX = rpc:call(Cpx, global_group, send, [{node, Cpxnn}, test, monitor]), - % Kill node Cp1 - ?line Pid2 = + %% Kill node Cp1 + Pid2 = rpc:call(Cp2, global_group, send, [{node, Cp2nn}, test2, {wait_nodedown, Cp1}]), - ?line PidX = + PidX = rpc:call(Cpx, global_group, send, [{node, Cpxnn}, test, {wait_nodedown, Cp1}]), - ?line test_server:sleep(100), - ?line stop_node(Cp1), - ?line test_server:sleep(1000), - - ?line ok = assert_loop(Cp2, Cp2nn, test2, Pid2, loop), - ?line ok = assert_loop(Cpx, Cpxnn, test, PidX, loop), + ct:sleep(100), + stop_node(Cp1), + ct:sleep(1000), + + ok = assert_loop(Cp2, Cp2nn, test2, Pid2, loop), + ok = assert_loop(Cpx, Cpxnn, test, PidX, loop), - % Kill node Cpz - ?line Pid2 = + %% Kill node Cpz + Pid2 = rpc:call(Cp2, global_group, send, [{node, Cp2nn}, test2, {wait_nodedown, Cpz}]), - ?line PidX = + PidX = rpc:call(Cpx, global_group, send, [{node, Cpxnn}, test, {wait_nodedown, Cpz}]), - ?line test_server:sleep(100), - ?line stop_node(Cpz), - ?line test_server:sleep(1000), - - ?line ok = assert_loop(Cp2, Cp2nn, test2, Pid2, loop), - ?line ok = assert_loop(Cpx, Cpxnn, test, PidX, loop), + ct:sleep(100), + stop_node(Cpz), + ct:sleep(1000), + + ok = assert_loop(Cp2, Cp2nn, test2, Pid2, loop), + ok = assert_loop(Cpx, Cpxnn, test, PidX, loop), - % Restart node Cp1 - ?line Pid2 = + %% Restart node Cp1 + Pid2 = rpc:call(Cp2, global_group, send, [{node, Cp2nn}, test2, {wait_nodeup, Cp1}]), - ?line PidX = + PidX = rpc:call(Cpx, global_group, send, [{node, Cpxnn}, test, {wait_nodeup, Cp1}]), - ?line {ok, Cp1} = start_node_comp(Ncp1, Config), - ?line pong = rpc:call(Cp2, net_adm, ping, [Cp1nn]), - ?line pong = rpc:call(Cpx, net_adm, ping, [Cp1nn]), - ?line wait_for_ready_net(), - - ?line ok = assert_loop(Cp2, Cp2nn, test2, Pid2, loop), - ?line ok = assert_loop(Cpx, Cpxnn, test, PidX, loop), + {ok, Cp1} = start_node_comp(Ncp1, Config), + pong = rpc:call(Cp2, net_adm, ping, [Cp1nn]), + pong = rpc:call(Cpx, net_adm, ping, [Cp1nn]), + wait_for_ready_net(), + + ok = assert_loop(Cp2, Cp2nn, test2, Pid2, loop), + ok = assert_loop(Cpx, Cpxnn, test, PidX, loop), - % Restart node Cpz - ?line Pid2 = + %% Restart node Cpz + Pid2 = rpc:call(Cp2, global_group, send, [{node, Cp2nn}, test2, {wait_nodeup, Cpz}]), - ?line PidX = + PidX = rpc:call(Cpx, global_group, send, [{node, Cpxnn}, test, {wait_nodeup, Cpz}]), - ?line {ok, Cpz} = start_node_comp(Ncpz, Config), - ?line pong = rpc:call(Cp2, net_adm, ping, [Cpznn]), - ?line pong = rpc:call(Cpx, net_adm, ping, [Cpznn]), - ?line wait_for_ready_net(), - - ?line ok = assert_loop(Cp2, Cp2nn, test2, Pid2, loop), - ?line ok = assert_loop(Cpx, Cpxnn, test, PidX, loop), + {ok, Cpz} = start_node_comp(Ncpz, Config), + pong = rpc:call(Cp2, net_adm, ping, [Cpznn]), + pong = rpc:call(Cpx, net_adm, ping, [Cpznn]), + wait_for_ready_net(), + + ok = assert_loop(Cp2, Cp2nn, test2, Pid2, loop), + ok = assert_loop(Cpx, Cpxnn, test, PidX, loop), - % stop the nodes, and make sure names are released. + %% stop the nodes, and make sure names are released. stop_node(Cp1), stop_node(Cp2), stop_node(Cp3), @@ -641,147 +625,137 @@ compatible(Config) when is_list(Config) -> stop_node(Cpy), stop_node(Cpz), - ?line ?UNTIL(undefined =:= global:whereis_name(test)), - ?line test_server:timetrap_cancel(Dog), + ?UNTIL(undefined =:= global:whereis_name(test)), ok. - -one_grp(suite) -> []; -one_grp(doc) -> ["Test a system with only one global group. "]; -one_grp(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(120)), - ?line Dir = ?config(priv_dir, Config), - ?line File = filename:join(Dir, "global_group.config"), - ?line {ok, Fd} = file:open(File, [write]), +%% Test a system with only one global group. . +one_grp(Config) when is_list(Config) -> + Dir = proplists:get_value(priv_dir, Config), + File = filename:join(Dir, "global_group.config"), + {ok, Fd} = file:open(File, [write]), [Ncp1,Ncp2,Ncp3] = node_names([cp1, cp2, cp3], Config), - ?line config(Fd, Ncp1, Ncp2, Ncp3, "cpx", "cpy", "cpz", "cpq"), + config(Fd, Ncp1, Ncp2, Ncp3, "cpx", "cpy", "cpz", "cpq"), - ?line {ok, Cp1} = start_node(Ncp1, Config), - ?line {ok, Cp2} = start_node(Ncp2, Config), - ?line {ok, Cp3} = start_node(Ncp3, Config), + {ok, Cp1} = start_node(Ncp1, Config), + {ok, Cp2} = start_node(Ncp2, Config), + {ok, Cp3} = start_node(Ncp3, Config), - % sleep a while to make the global_group to sync... - test_server:sleep(1000), + %% sleep a while to make the global_group to sync... + ct:sleep(1000), - % start a proc and register it - ?line {Pid, yes} = rpc:call(Cp1, ?MODULE, start_proc, [test]), + %% start a proc and register it + {Pid, yes} = rpc:call(Cp1, ?MODULE, start_proc, [test]), - % test that it is registered at all nodes - ?line Pid = rpc:call(Cp1, global, whereis_name, [test]), - ?line Pid = rpc:call(Cp2, global, whereis_name, [test]), - ?line Pid = rpc:call(Cp3, global, whereis_name, [test]), + %% test that it is registered at all nodes + Pid = rpc:call(Cp1, global, whereis_name, [test]), + Pid = rpc:call(Cp2, global, whereis_name, [test]), + Pid = rpc:call(Cp3, global, whereis_name, [test]), - % try to register the same name - ?line no = rpc:call(Cp1, global, register_name, [test, self()]), + %% try to register the same name + no = rpc:call(Cp1, global, register_name, [test, self()]), - % let process exit, check that it is unregistered automatically + %% let process exit, check that it is unregistered automatically Pid ! die, - ?line - ?UNTIL(begin + ?UNTIL(begin (undefined =:= rpc:call(Cp1, global, whereis_name, [test])) and (undefined =:= rpc:call(Cp2, global, whereis_name, [test])) and (undefined =:= rpc:call(Cp3, global, whereis_name, [test])) end), - % test re_register - ?line {Pid2, yes} = rpc:call(Cp1, ?MODULE, start_proc, [test]), - ?line Pid2 = rpc:call(Cp3, global, whereis_name, [test]), + %% test re_register + {Pid2, yes} = rpc:call(Cp1, ?MODULE, start_proc, [test]), + Pid2 = rpc:call(Cp3, global, whereis_name, [test]), Pid3 = rpc:call(Cp3, ?MODULE, start_proc_rereg, [test]), - ?line Pid3 = rpc:call(Cp3, global, whereis_name, [test]), + Pid3 = rpc:call(Cp3, global, whereis_name, [test]), - % test sending + %% test sending rpc:call(Cp1, global, send, [test, {ping, self()}]), receive {pong, Cp3} -> ok after - 2000 -> test_server:fail(timeout1) + 2000 -> ct:fail(timeout1) end, rpc:call(Cp3, global, send, [test, {ping, self()}]), receive {pong, Cp3} -> ok after - 2000 -> test_server:fail(timeout2) + 2000 -> ct:fail(timeout2) end, - ?line rpc:call(Cp3, global, unregister_name, [test]), - ?line undefined = rpc:call(Cp1, global, whereis_name, [test]), - ?line undefined = rpc:call(Cp2, global, whereis_name, [test]), - ?line undefined = rpc:call(Cp3, global, whereis_name, [test]), + rpc:call(Cp3, global, unregister_name, [test]), + undefined = rpc:call(Cp1, global, whereis_name, [test]), + undefined = rpc:call(Cp2, global, whereis_name, [test]), + undefined = rpc:call(Cp3, global, whereis_name, [test]), Pid3 ! die, - ?line ?UNTIL(undefined =:= rpc:call(Cp3, global, whereis_name, [test])), + ?UNTIL(undefined =:= rpc:call(Cp3, global, whereis_name, [test])), - % register a proc - ?line {_, yes} = rpc:call(Cp3, ?MODULE, start_proc, [test]), + %% register a proc + {_, yes} = rpc:call(Cp3, ?MODULE, start_proc, [test]), - % stop the nodes, and make sure names are released. + %% stop the nodes, and make sure names are released. stop_node(Cp3), - ?line ?UNTIL(undefined =:= rpc:call(Cp1, global, whereis_name, [test])), + ?UNTIL(undefined =:= rpc:call(Cp1, global, whereis_name, [test])), Pid2 ! die, stop_node(Cp1), stop_node(Cp2), - ?line test_server:timetrap_cancel(Dog), ok. - -one_grp_x(suite) -> []; -one_grp_x(doc) -> ["Check a system with only one global group. " - "Start the nodes with different time intervals. "]; -one_grp_x(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(120)), - ?line Dir = ?config(priv_dir, Config), - ?line File = filename:join(Dir, "global_group.config"), - ?line {ok, Fd} = file:open(File, [write]), +%% Check a system with only one global group. +%% Start the nodes with different time intervals. +one_grp_x(Config) when is_list(Config) -> + Dir = proplists:get_value(priv_dir, Config), + File = filename:join(Dir, "global_group.config"), + {ok, Fd} = file:open(File, [write]), [Ncp1,Ncp2,Ncp3] = node_names([cp1, cp2, cp3], Config), - ?line config(Fd, Ncp1, Ncp2, Ncp3, "cpx", "cpy", "cpz", "cpq"), + config(Fd, Ncp1, Ncp2, Ncp3, "cpx", "cpy", "cpz", "cpq"), - ?line {ok, Cp1} = start_node(Ncp1, Config), - % sleep a while to make the global_group to sync... - test_server:sleep(1000), + {ok, Cp1} = start_node(Ncp1, Config), + %% sleep a while to make the global_group to sync... + ct:sleep(1000), - % start a proc and register it - ?line {Pid, yes} = rpc:call(Cp1, ?MODULE, start_proc, [test]), + %% start a proc and register it + {Pid, yes} = rpc:call(Cp1, ?MODULE, start_proc, [test]), - ?line {ok, Cp2} = start_node(Ncp2, Config), - % sleep a while to make the global_group to sync... - test_server:sleep(1000), + {ok, Cp2} = start_node(Ncp2, Config), + %% sleep a while to make the global_group to sync... + ct:sleep(1000), - % test that it is registered at all nodes - ?line Pid = rpc:call(Cp1, global, whereis_name, [test]), - ?line Pid = rpc:call(Cp2, global, whereis_name, [test]), + %% test that it is registered at all nodes + Pid = rpc:call(Cp1, global, whereis_name, [test]), + Pid = rpc:call(Cp2, global, whereis_name, [test]), - ?line {ok, Cp3} = start_node(Ncp3, Config), - % sleep a while to make the global_group to sync... - test_server:sleep(1000), + {ok, Cp3} = start_node(Ncp3, Config), + %% sleep a while to make the global_group to sync... + ct:sleep(1000), - ?line Pid = rpc:call(Cp3, global, whereis_name, [test]), + Pid = rpc:call(Cp3, global, whereis_name, [test]), - % try to register the same name - ?line no = rpc:call(Cp1, global, register_name, [test, self()]), + %% try to register the same name + no = rpc:call(Cp1, global, register_name, [test, self()]), - % let process exit, check that it is unregistered automatically + %% let process exit, check that it is unregistered automatically Pid ! die, - ?line - ?UNTIL(begin + ?UNTIL(begin (undefined =:= rpc:call(Cp1, global, whereis_name, [test])) and (undefined =:= rpc:call(Cp2, global, whereis_name, [test])) and (undefined =:= rpc:call(Cp3, global, whereis_name, [test])) end), - % test re_register - ?line {Pid2, yes} = rpc:call(Cp1, ?MODULE, start_proc, [test]), - ?line Pid2 = rpc:call(Cp3, global, whereis_name, [test]), + %% test re_register + {Pid2, yes} = rpc:call(Cp1, ?MODULE, start_proc, [test]), + Pid2 = rpc:call(Cp3, global, whereis_name, [test]), Pid2 ! die, @@ -789,296 +763,291 @@ one_grp_x(Config) when is_list(Config) -> stop_node(Cp2), stop_node(Cp3), - ?line test_server:timetrap_cancel(Dog), ok. - -two_grp(suite) -> []; -two_grp(doc) -> ["Test a two global group system. "]; -two_grp(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(200)), - ?line Dir = ?config(priv_dir, Config), - ?line File = filename:join(Dir, "global_group.config"), - ?line {ok, Fd} = file:open(File, [write]), +%% Test a two global group system. . +two_grp(Config) when is_list(Config) -> + Dir = proplists:get_value(priv_dir, Config), + File = filename:join(Dir, "global_group.config"), + {ok, Fd} = file:open(File, [write]), [Ncp1,Ncp2,Ncp3,Ncpx,Ncpy,Ncpz,Ncpq] = node_names([cp1,cp2,cp3,cpx,cpy,cpz,cpq], Config), - ?line config(Fd, Ncp1, Ncp2, Ncp3, Ncpx, Ncpy, Ncpz, Ncpq), - - ?line Cp1nn = node_at(Ncp1), - ?line Cp2nn = node_at(Ncp2), - ?line Cp3nn = node_at(Ncp3), - ?line Cpxnn = node_at(Ncpx), - ?line Cpynn = node_at(Ncpy), - ?line Cpznn = node_at(Ncpz), - - ?line {ok, Cp1} = start_node(Ncp1, Config), - ?line {ok, Cp2} = start_node(Ncp2, Config), - ?line {ok, Cp3} = start_node(Ncp3, Config), - ?line {ok, Cpx} = start_node(Ncpx, Config), - ?line {ok, Cpy} = start_node(Ncpy, Config), - ?line {ok, Cpz} = start_node(Ncpz, Config), + config(Fd, Ncp1, Ncp2, Ncp3, Ncpx, Ncpy, Ncpz, Ncpq), + + Cp1nn = node_at(Ncp1), + Cp2nn = node_at(Ncp2), + Cp3nn = node_at(Ncp3), + Cpxnn = node_at(Ncpx), + Cpynn = node_at(Ncpy), + Cpznn = node_at(Ncpz), + + {ok, Cp1} = start_node(Ncp1, Config), + {ok, Cp2} = start_node(Ncp2, Config), + {ok, Cp3} = start_node(Ncp3, Config), + {ok, Cpx} = start_node(Ncpx, Config), + {ok, Cpy} = start_node(Ncpy, Config), + {ok, Cpz} = start_node(Ncpz, Config), %% The groups (cpq not started): %% [{nc1, [cp1,cp2,cp3]}, {nc2, [cpx,cpy,cpz]}, {nc3, [cpq]}] - % sleep a while to make the global_groups to sync... - test_server:sleep(1000), - - % check the global group names - ?line {nc1, [nc2, nc3]} = rpc:call(Cp1, global_group, global_groups, []), - ?line {nc1, [nc2, nc3]} = rpc:call(Cp2, global_group, global_groups, []), - ?line {nc1, [nc2, nc3]} = rpc:call(Cp3, global_group, global_groups, []), - ?line {nc2, [nc1, nc3]} = rpc:call(Cpx, global_group, global_groups, []), - ?line {nc2, [nc1, nc3]} = rpc:call(Cpy, global_group, global_groups, []), - ?line {nc2, [nc1, nc3]} = rpc:call(Cpz, global_group, global_groups, []), - - % check the global group nodes - ?line [Cp1nn, Cp2nn, Cp3nn] = rpc:call(Cp1, global_group, own_nodes, []), - ?line [Cp1nn, Cp2nn, Cp3nn] = rpc:call(Cp2, global_group, own_nodes, []), - ?line [Cp1nn, Cp2nn, Cp3nn] = rpc:call(Cp3, global_group, own_nodes, []), - ?line [Cpxnn, Cpynn, Cpznn] = rpc:call(Cpx, global_group, own_nodes, []), - ?line [Cpxnn, Cpynn, Cpznn] = rpc:call(Cpy, global_group, own_nodes, []), - ?line [Cpxnn, Cpynn, Cpznn] = rpc:call(Cpz, global_group, own_nodes, []), - - - % start a proc and register it - ?line {Pid1, yes} = rpc:call(Cp1, ?MODULE, start_proc, [test]), - - ?line Pid1 = rpc:call(Cp1, global_group, send, [test, {io, from_cp1}]), - ?line Pid1 = rpc:call(Cpx, global_group, send, [test, {io, from_cpx}]), - ?line Pid1 = rpc:call(Cp1, global_group, send, [{group,nc1}, test, - {io, from_cp1}]), - ?line [test] = + %% sleep a while to make the global_groups to sync... + ct:sleep(1000), + + %% check the global group names + {nc1, [nc2, nc3]} = rpc:call(Cp1, global_group, global_groups, []), + {nc1, [nc2, nc3]} = rpc:call(Cp2, global_group, global_groups, []), + {nc1, [nc2, nc3]} = rpc:call(Cp3, global_group, global_groups, []), + {nc2, [nc1, nc3]} = rpc:call(Cpx, global_group, global_groups, []), + {nc2, [nc1, nc3]} = rpc:call(Cpy, global_group, global_groups, []), + {nc2, [nc1, nc3]} = rpc:call(Cpz, global_group, global_groups, []), + + %% check the global group nodes + [Cp1nn, Cp2nn, Cp3nn] = rpc:call(Cp1, global_group, own_nodes, []), + [Cp1nn, Cp2nn, Cp3nn] = rpc:call(Cp2, global_group, own_nodes, []), + [Cp1nn, Cp2nn, Cp3nn] = rpc:call(Cp3, global_group, own_nodes, []), + [Cpxnn, Cpynn, Cpznn] = rpc:call(Cpx, global_group, own_nodes, []), + [Cpxnn, Cpynn, Cpznn] = rpc:call(Cpy, global_group, own_nodes, []), + [Cpxnn, Cpynn, Cpznn] = rpc:call(Cpz, global_group, own_nodes, []), + + + %% start a proc and register it + {Pid1, yes} = rpc:call(Cp1, ?MODULE, start_proc, [test]), + + Pid1 = rpc:call(Cp1, global_group, send, [test, {io, from_cp1}]), + Pid1 = rpc:call(Cpx, global_group, send, [test, {io, from_cpx}]), + Pid1 = rpc:call(Cp1, global_group, send, [{group,nc1}, test, + {io, from_cp1}]), + [test] = rpc:call(Cpx, global_group, registered_names, [{node, Cp1nn}]), - ?line [test] = + [test] = rpc:call(Cpx, global_group, registered_names, [{group, nc1}]), - ?line [] = rpc:call(Cpx, global_group, registered_names, [{node, Cpxnn}]), - ?line [] = rpc:call(Cpx, global_group, registered_names, [{group, nc2}]), - ?line Pid1 = rpc:call(Cpx, global_group, send, [{group,nc1}, test, - {io, from_cp1}]), - ?line {badarg,{test,{io,from_cpx}}} = + [] = rpc:call(Cpx, global_group, registered_names, [{node, Cpxnn}]), + [] = rpc:call(Cpx, global_group, registered_names, [{group, nc2}]), + Pid1 = rpc:call(Cpx, global_group, send, [{group,nc1}, test, + {io, from_cp1}]), + {badarg,{test,{io,from_cpx}}} = rpc:call(Cp1, global_group, send, [{group,nc2}, test, {io, from_cpx}]), - ?line {badarg,{test,{io,from_cpx}}} = + {badarg,{test,{io,from_cpx}}} = rpc:call(Cpx, global_group, send, [{group,nc2}, test, {io, from_cpx}]), - % test that it is registered at all nodes - ?line Pid1 = rpc:call(Cp1, global, whereis_name, [test]), - ?line Pid1 = rpc:call(Cp2, global, whereis_name, [test]), - ?line Pid1 = rpc:call(Cp3, global, whereis_name, [test]), - ?line undefined = rpc:call(Cpx, global, whereis_name, [test]), - ?line undefined = rpc:call(Cpy, global, whereis_name, [test]), - ?line undefined = rpc:call(Cpz, global, whereis_name, [test]), - - % start a proc and register it - ?line {PidX, yes} = rpc:call(Cpx, ?MODULE, start_proc, [test]), - - % test that it is registered at all nodes - ?line Pid1 = rpc:call(Cp1, global, whereis_name, [test]), - ?line Pid1 = rpc:call(Cp2, global, whereis_name, [test]), - ?line Pid1 = rpc:call(Cp3, global, whereis_name, [test]), - ?line PidX = rpc:call(Cpx, global, whereis_name, [test]), - ?line PidX = rpc:call(Cpy, global, whereis_name, [test]), - ?line PidX = rpc:call(Cpz, global, whereis_name, [test]), - + %% test that it is registered at all nodes + Pid1 = rpc:call(Cp1, global, whereis_name, [test]), + Pid1 = rpc:call(Cp2, global, whereis_name, [test]), + Pid1 = rpc:call(Cp3, global, whereis_name, [test]), + undefined = rpc:call(Cpx, global, whereis_name, [test]), + undefined = rpc:call(Cpy, global, whereis_name, [test]), + undefined = rpc:call(Cpz, global, whereis_name, [test]), + + %% start a proc and register it + {PidX, yes} = rpc:call(Cpx, ?MODULE, start_proc, [test]), + + %% test that it is registered at all nodes + Pid1 = rpc:call(Cp1, global, whereis_name, [test]), + Pid1 = rpc:call(Cp2, global, whereis_name, [test]), + Pid1 = rpc:call(Cp3, global, whereis_name, [test]), + PidX = rpc:call(Cpx, global, whereis_name, [test]), + PidX = rpc:call(Cpy, global, whereis_name, [test]), + PidX = rpc:call(Cpz, global, whereis_name, [test]), + Pid1 ! die, %% If we don't wait for global on other nodes to have updated its %% tables, 'test' may still be defined at the point when it is %% tested a few lines below. - ?line - ?UNTIL(begin + ?UNTIL(begin Pid = rpc:call(Cp2, global, whereis_name, [test]), undefined =:= Pid end), - % start a proc and register it - ?line {Pid2, yes} = rpc:call(Cp2, ?MODULE, start_proc, [test2]), + %% start a proc and register it + {Pid2, yes} = rpc:call(Cp2, ?MODULE, start_proc, [test2]), + + %% test that it is registered at all nodes + Pid2 = rpc:call(Cp1, global, whereis_name, [test2]), + Pid2 = rpc:call(Cp2, global, whereis_name, [test2]), + Pid2 = rpc:call(Cp3, global, whereis_name, [test2]), + PidX = rpc:call(Cpx, global, whereis_name, [test]), + PidX = rpc:call(Cpy, global, whereis_name, [test]), + PidX = rpc:call(Cpz, global, whereis_name, [test]), + + undefined = rpc:call(Cp1, global, whereis_name, [test]), + undefined = rpc:call(Cp2, global, whereis_name, [test]), + undefined = rpc:call(Cp3, global, whereis_name, [test]), + undefined = rpc:call(Cpx, global, whereis_name, [test2]), + undefined = rpc:call(Cpy, global, whereis_name, [test2]), + undefined = rpc:call(Cpz, global, whereis_name, [test2]), - % test that it is registered at all nodes - ?line Pid2 = rpc:call(Cp1, global, whereis_name, [test2]), - ?line Pid2 = rpc:call(Cp2, global, whereis_name, [test2]), - ?line Pid2 = rpc:call(Cp3, global, whereis_name, [test2]), - ?line PidX = rpc:call(Cpx, global, whereis_name, [test]), - ?line PidX = rpc:call(Cpy, global, whereis_name, [test]), - ?line PidX = rpc:call(Cpz, global, whereis_name, [test]), - - ?line undefined = rpc:call(Cp1, global, whereis_name, [test]), - ?line undefined = rpc:call(Cp2, global, whereis_name, [test]), - ?line undefined = rpc:call(Cp3, global, whereis_name, [test]), - ?line undefined = rpc:call(Cpx, global, whereis_name, [test2]), - ?line undefined = rpc:call(Cpy, global, whereis_name, [test2]), - ?line undefined = rpc:call(Cpz, global, whereis_name, [test2]), - - ?line Pid2 = rpc:call(Cp1, global_group, send, [test2, {ping, self()}]), + Pid2 = rpc:call(Cp1, global_group, send, [test2, {ping, self()}]), receive {pong, Cp2} -> ok after - 2000 -> test_server:fail(timeout2) + 2000 -> ct:fail(timeout2) end, - ?line Pid2 = rpc:call(Cp2, global_group, send, [test2, {ping, self()}]), + Pid2 = rpc:call(Cp2, global_group, send, [test2, {ping, self()}]), receive {pong, Cp2} -> ok after - 2000 -> test_server:fail(timeout2) + 2000 -> ct:fail(timeout2) end, - ?line Pid2 = rpc:call(Cp3, global_group, send, [test2, {ping, self()}]), + Pid2 = rpc:call(Cp3, global_group, send, [test2, {ping, self()}]), receive {pong, Cp2} -> ok after - 2000 -> test_server:fail(timeout2) + 2000 -> ct:fail(timeout2) end, - ?line PidX = rpc:call(Cpx, global_group, send, [test, {ping, self()}]), + PidX = rpc:call(Cpx, global_group, send, [test, {ping, self()}]), receive {pong, Cpx} -> ok after - 2000 -> test_server:fail(timeout2) + 2000 -> ct:fail(timeout2) end, - ?line PidX = rpc:call(Cpy, global_group, send, [test, {ping, self()}]), + PidX = rpc:call(Cpy, global_group, send, [test, {ping, self()}]), receive {pong, Cpx} -> ok after - 2000 -> test_server:fail(timeout2) + 2000 -> ct:fail(timeout2) end, - ?line PidX = rpc:call(Cpz, global_group, send, [test, {ping, self()}]), + PidX = rpc:call(Cpz, global_group, send, [test, {ping, self()}]), receive {pong, Cpx} -> ok after - 2000 -> test_server:fail(timeout2) + 2000 -> ct:fail(timeout2) end, - ?line Pid2 = rpc:call(Cpx, global_group, send, [{node, Cp1nn}, test2, - {ping, self()}]), + Pid2 = rpc:call(Cpx, global_group, send, [{node, Cp1nn}, test2, + {ping, self()}]), receive {pong, Cp2} -> ok after - 2000 -> test_server:fail(timeout2) + 2000 -> ct:fail(timeout2) end, - ?line Pid2 = rpc:call(Cpy, global_group, send, [{node, Cp2nn}, test2, - {ping, self()}]), + Pid2 = rpc:call(Cpy, global_group, send, [{node, Cp2nn}, test2, + {ping, self()}]), receive {pong, Cp2} -> ok after - 2000 -> test_server:fail(timeout2) + 2000 -> ct:fail(timeout2) end, - ?line Pid2 = rpc:call(Cpz, global_group, send, [{node, Cp3nn}, test2, - {ping, self()}]), + Pid2 = rpc:call(Cpz, global_group, send, [{node, Cp3nn}, test2, + {ping, self()}]), receive {pong, Cp2} -> ok after - 2000 -> test_server:fail(timeout2) + 2000 -> ct:fail(timeout2) end, - ?line PidX = rpc:call(Cpx, global_group, send, [{node, Cpznn}, test, - {ping, self()}]), + PidX = rpc:call(Cpx, global_group, send, [{node, Cpznn}, test, + {ping, self()}]), receive {pong, Cpx} -> ok after - 2000 -> test_server:fail(timeout2) + 2000 -> ct:fail(timeout2) end, - ?line PidX = rpc:call(Cpy, global_group, send, [{node, Cpxnn}, test, - {ping, self()}]), + PidX = rpc:call(Cpy, global_group, send, [{node, Cpxnn}, test, + {ping, self()}]), receive {pong, Cpx} -> ok after - 2000 -> test_server:fail(timeout2) + 2000 -> ct:fail(timeout2) end, - ?line PidX = rpc:call(Cpz, global_group, send, [{node, Cpynn}, test, - {ping, self()}]), + PidX = rpc:call(Cpz, global_group, send, [{node, Cpynn}, test, + {ping, self()}]), receive {pong, Cpx} -> ok after - 2000 -> test_server:fail(timeout2) + 2000 -> ct:fail(timeout2) end, - ?line Pid2 = rpc:call(Cpx, global_group, send, [{group, nc1}, test2, - {ping, self()}]), + Pid2 = rpc:call(Cpx, global_group, send, [{group, nc1}, test2, + {ping, self()}]), receive {pong, Cp2} -> ok after - 2000 -> test_server:fail(timeout2) + 2000 -> ct:fail(timeout2) end, - ?line PidX = rpc:call(Cpy, global_group, send, [{group, nc2}, test, - {ping, self()}]), + PidX = rpc:call(Cpy, global_group, send, [{group, nc2}, test, + {ping, self()}]), receive {pong, Cpx} -> ok after - 2000 -> test_server:fail(timeout2) + 2000 -> ct:fail(timeout2) end, %%------------------------------------ %% Test monitor nodes %%------------------------------------ - ?line Pid2 = + Pid2 = rpc:call(Cp1, global_group, send, [{node, Cp2nn}, test2, monitor]), - ?line PidX = + PidX = rpc:call(Cpx, global_group, send, [{node, Cpxnn}, test, monitor]), - % Kill node Cp1 - ?line Pid2 = rpc:call(Cp2, global_group, send, [{node, Cp2nn}, test2, - {wait_nodedown, Cp1}]), - ?line PidX = rpc:call(Cpx, global_group, send, [{node, Cpxnn}, test, - {wait_nodedown, Cp1}]), - ?line test_server:sleep(100), - ?line stop_node(Cp1), - ?line test_server:sleep(1000), - - ?line ok = assert_loop(Cp2, Cp2nn, test2, Pid2, loop), - ?line ok = assert_loop(Cpx, Cpxnn, test, PidX, loop_nodedown), - ?line PidX = + %% Kill node Cp1 + Pid2 = rpc:call(Cp2, global_group, send, [{node, Cp2nn}, test2, + {wait_nodedown, Cp1}]), + PidX = rpc:call(Cpx, global_group, send, [{node, Cpxnn}, test, + {wait_nodedown, Cp1}]), + ct:sleep(100), + stop_node(Cp1), + ct:sleep(1000), + + ok = assert_loop(Cp2, Cp2nn, test2, Pid2, loop), + ok = assert_loop(Cpx, Cpxnn, test, PidX, loop_nodedown), + PidX = rpc:call(Cpx, global_group, send, [{node, Cpxnn}, test, to_loop]), - % Kill node Cpz - ?line Pid2 = rpc:call(Cp2, global_group, send, [{node, Cp2nn}, test2, - {wait_nodedown, Cpz}]), - ?line PidX = rpc:call(Cpx, global_group, send, [{node, Cpxnn}, test, - {wait_nodedown, Cpz}]), - ?line test_server:sleep(100), - ?line stop_node(Cpz), - ?line test_server:sleep(1000), - - ?line ok = assert_loop(Cp2, Cp2nn, test2, Pid2, loop_nodedown), - ?line ok = assert_loop(Cpx, Cpxnn, test, PidX, loop), - ?line Pid2 = + %% Kill node Cpz + Pid2 = rpc:call(Cp2, global_group, send, [{node, Cp2nn}, test2, + {wait_nodedown, Cpz}]), + PidX = rpc:call(Cpx, global_group, send, [{node, Cpxnn}, test, + {wait_nodedown, Cpz}]), + ct:sleep(100), + stop_node(Cpz), + ct:sleep(1000), + + ok = assert_loop(Cp2, Cp2nn, test2, Pid2, loop_nodedown), + ok = assert_loop(Cpx, Cpxnn, test, PidX, loop), + Pid2 = rpc:call(Cp2, global_group, send, [{node, Cp2nn}, test2, to_loop]), - % Restart node Cp1 - ?line [Cp1nn, Cp2nn, Cp3nn] = rpc:call(Cp2, global_group, own_nodes, []), - ?line Pid2 = rpc:call(Cp2, global_group, send, [{node, Cp2nn}, test2, - {wait_nodeup, Cp1}]), - ?line PidX = rpc:call(Cpx, global_group, send, [{node, Cpxnn}, test, - {wait_nodeup, Cp1}]), - ?line test_server:sleep(100), - ?line {ok, Cp1} = start_node(Ncp1, Config), - ?line test_server:sleep(5000), - - ?line ok = assert_loop(Cp2, Cp2nn, test2, Pid2, loop), - ?line ok = assert_loop(Cpx, Cpxnn, test, PidX, loop_nodeup), - ?line PidX = + %% Restart node Cp1 + [Cp1nn, Cp2nn, Cp3nn] = rpc:call(Cp2, global_group, own_nodes, []), + Pid2 = rpc:call(Cp2, global_group, send, [{node, Cp2nn}, test2, + {wait_nodeup, Cp1}]), + PidX = rpc:call(Cpx, global_group, send, [{node, Cpxnn}, test, + {wait_nodeup, Cp1}]), + ct:sleep(100), + {ok, Cp1} = start_node(Ncp1, Config), + ct:sleep(5000), + + ok = assert_loop(Cp2, Cp2nn, test2, Pid2, loop), + ok = assert_loop(Cpx, Cpxnn, test, PidX, loop_nodeup), + PidX = rpc:call(Cpx, global_group, send, [{node, Cpxnn}, test, to_loop]), - % Restart node Cpz - ?line Pid2 = rpc:call(Cp2, global_group, send, [{node, Cp2nn}, test2, - {wait_nodeup, Cpz}]), - ?line PidX = rpc:call(Cpx, global_group, send, [{node, Cpxnn}, test, - {wait_nodeup, Cpz}]), - ?line test_server:sleep(100), - ?line {ok, Cpz} = start_node(Ncpz, Config), - ?line test_server:sleep(5000), - - ?line ok = assert_loop(Cp2, Cp2nn, test2, Pid2, loop_nodeup), - ?line ok = assert_loop(Cpx, Cpxnn, test, PidX, loop), - ?line Pid2 = + %% Restart node Cpz + Pid2 = rpc:call(Cp2, global_group, send, [{node, Cp2nn}, test2, + {wait_nodeup, Cpz}]), + PidX = rpc:call(Cpx, global_group, send, [{node, Cpxnn}, test, + {wait_nodeup, Cpz}]), + ct:sleep(100), + {ok, Cpz} = start_node(Ncpz, Config), + ct:sleep(5000), + + ok = assert_loop(Cp2, Cp2nn, test2, Pid2, loop_nodeup), + ok = assert_loop(Cpx, Cpxnn, test, PidX, loop), + Pid2 = rpc:call(Cp2, global_group, send, [{node, Cp2nn}, test2, to_loop]), @@ -1092,72 +1061,68 @@ two_grp(Config) when is_list(Config) -> stop_node(Cpy), stop_node(Cpz), - ?line test_server:timetrap_cancel(Dog), ok. - -hidden_groups(suite) -> []; -hidden_groups(doc) -> ["Test hidden global groups."]; -hidden_groups(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(200)), - ?line Dir = ?config(priv_dir, Config), - ?line File = filename:join(Dir, "global_group.config"), - ?line {ok, Fd} = file:open(File, [write]), +%% Test hidden global groups. +hidden_groups(Config) when is_list(Config) -> + Dir = proplists:get_value(priv_dir, Config), + File = filename:join(Dir, "global_group.config"), + {ok, Fd} = file:open(File, [write]), [Ncp1,Ncp2,Ncp3,Ncpx,Ncpy,Ncpz,Ncpq] = node_names([cp1,cp2,cp3,cpx,cpy,cpz,cpq], Config), - ?line config_hidden(Fd, Ncp1, Ncp2, Ncp3, Ncpx, Ncpy, Ncpz, Ncpq), - - ?line {ok, Cp1} = start_node(Ncp1, Config), - ?line {ok, Cp2} = start_node(Ncp2, Config), - ?line {ok, Cp3} = start_node(Ncp3, Config), - ?line {ok, Cpx} = start_node(Ncpx, Config), - ?line {ok, Cpy} = start_node(Ncpy, Config), - ?line {ok, Cpz} = start_node(Ncpz, Config), - ?line {ok, Cpq} = start_node(Ncpq, Config), - - % sleep a while to make the global_groups to sync... - test_server:sleep(1000), - - % check the global group names - ?line {nc1, [nc2, nc3]} = rpc:call(Cp1, global_group, global_groups, []), - ?line {nc1, [nc2, nc3]} = rpc:call(Cp2, global_group, global_groups, []), - ?line {nc1, [nc2, nc3]} = rpc:call(Cp3, global_group, global_groups, []), - ?line {nc2, [nc1, nc3]} = rpc:call(Cpx, global_group, global_groups, []), - ?line {nc2, [nc1, nc3]} = rpc:call(Cpy, global_group, global_groups, []), - ?line {nc2, [nc1, nc3]} = rpc:call(Cpz, global_group, global_groups, []), - - % check the global group nodes - ?line [Cp1, Cp2, Cp3] = rpc:call(Cp1, global_group, own_nodes, []), - ?line [Cp1, Cp2, Cp3] = rpc:call(Cp2, global_group, own_nodes, []), - ?line [Cp1, Cp2, Cp3] = rpc:call(Cp3, global_group, own_nodes, []), - ?line [Cpx, Cpy, Cpz] = rpc:call(Cpx, global_group, own_nodes, []), - ?line [Cpx, Cpy, Cpz] = rpc:call(Cpy, global_group, own_nodes, []), - ?line [Cpx, Cpy, Cpz] = rpc:call(Cpz, global_group, own_nodes, []), - ?line [Cpq] = rpc:call(Cpq, global_group, own_nodes, []), - - % Make some inter group connections - ?line pong = rpc:call(Cp1, net_adm, ping, [Cpx]), - ?line pong = rpc:call(Cpy, net_adm, ping, [Cp2]), - ?line pong = rpc:call(Cp3, net_adm, ping, [Cpx]), - ?line pong = rpc:call(Cpz, net_adm, ping, [Cp3]), - ?line pong = rpc:call(Cpq, net_adm, ping, [Cp1]), - ?line pong = rpc:call(Cpz, net_adm, ping, [Cpq]), - - % Check that no inter group connections are visible + config_hidden(Fd, Ncp1, Ncp2, Ncp3, Ncpx, Ncpy, Ncpz, Ncpq), + + {ok, Cp1} = start_node(Ncp1, Config), + {ok, Cp2} = start_node(Ncp2, Config), + {ok, Cp3} = start_node(Ncp3, Config), + {ok, Cpx} = start_node(Ncpx, Config), + {ok, Cpy} = start_node(Ncpy, Config), + {ok, Cpz} = start_node(Ncpz, Config), + {ok, Cpq} = start_node(Ncpq, Config), + + %% sleep a while to make the global_groups to sync... + ct:sleep(1000), + + %% check the global group names + {nc1, [nc2, nc3]} = rpc:call(Cp1, global_group, global_groups, []), + {nc1, [nc2, nc3]} = rpc:call(Cp2, global_group, global_groups, []), + {nc1, [nc2, nc3]} = rpc:call(Cp3, global_group, global_groups, []), + {nc2, [nc1, nc3]} = rpc:call(Cpx, global_group, global_groups, []), + {nc2, [nc1, nc3]} = rpc:call(Cpy, global_group, global_groups, []), + {nc2, [nc1, nc3]} = rpc:call(Cpz, global_group, global_groups, []), + + %% check the global group nodes + [Cp1, Cp2, Cp3] = rpc:call(Cp1, global_group, own_nodes, []), + [Cp1, Cp2, Cp3] = rpc:call(Cp2, global_group, own_nodes, []), + [Cp1, Cp2, Cp3] = rpc:call(Cp3, global_group, own_nodes, []), + [Cpx, Cpy, Cpz] = rpc:call(Cpx, global_group, own_nodes, []), + [Cpx, Cpy, Cpz] = rpc:call(Cpy, global_group, own_nodes, []), + [Cpx, Cpy, Cpz] = rpc:call(Cpz, global_group, own_nodes, []), + [Cpq] = rpc:call(Cpq, global_group, own_nodes, []), + + %% Make some inter group connections + pong = rpc:call(Cp1, net_adm, ping, [Cpx]), + pong = rpc:call(Cpy, net_adm, ping, [Cp2]), + pong = rpc:call(Cp3, net_adm, ping, [Cpx]), + pong = rpc:call(Cpz, net_adm, ping, [Cp3]), + pong = rpc:call(Cpq, net_adm, ping, [Cp1]), + pong = rpc:call(Cpz, net_adm, ping, [Cpq]), + + %% Check that no inter group connections are visible NC1Nodes = lists:sort([Cp1, Cp2, Cp3]), NC2Nodes = lists:sort([Cpx, Cpy, Cpz]), - ?line NC1Nodes = lists:sort([Cp1|rpc:call(Cp1, erlang, nodes, [])]), - ?line NC1Nodes = lists:sort([Cp2|rpc:call(Cp2, erlang, nodes, [])]), - ?line NC1Nodes = lists:sort([Cp3|rpc:call(Cp3, erlang, nodes, [])]), - ?line NC2Nodes = lists:sort([Cpx|rpc:call(Cpx, erlang, nodes, [])]), - ?line NC2Nodes = lists:sort([Cpy|rpc:call(Cpy, erlang, nodes, [])]), - ?line NC2Nodes = lists:sort([Cpz|rpc:call(Cpz, erlang, nodes, [])]), + NC1Nodes = lists:sort([Cp1|rpc:call(Cp1, erlang, nodes, [])]), + NC1Nodes = lists:sort([Cp2|rpc:call(Cp2, erlang, nodes, [])]), + NC1Nodes = lists:sort([Cp3|rpc:call(Cp3, erlang, nodes, [])]), + NC2Nodes = lists:sort([Cpx|rpc:call(Cpx, erlang, nodes, [])]), + NC2Nodes = lists:sort([Cpy|rpc:call(Cpy, erlang, nodes, [])]), + NC2Nodes = lists:sort([Cpz|rpc:call(Cpz, erlang, nodes, [])]), NC12Nodes = lists:append(NC1Nodes, NC2Nodes), - ?line false = lists:any(fun(N) -> lists:member(N, NC12Nodes) end, - rpc:call(Cpq, erlang, nodes, [])), + false = lists:any(fun(N) -> lists:member(N, NC12Nodes) end, + rpc:call(Cpq, erlang, nodes, [])), stop_node(Cp1), @@ -1168,63 +1133,68 @@ hidden_groups(Config) when is_list(Config) -> stop_node(Cpz), stop_node(Cpq), - ?line test_server:timetrap_cancel(Dog), ok. - -test_exit(suite) -> []; -test_exit(doc) -> ["Checks when the search process exits. "]; -test_exit(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(120)), - ?line NN = node_name(atom_to_list(node())), - ?line Cp1nn = list_to_atom("cp1@" ++ NN), +%% Checks when the search process exits. . +test_exit(Config) when is_list(Config) -> + NN = node_name(atom_to_list(node())), + Cp1nn = list_to_atom("cp1@" ++ NN), - ?line {ok, Cp1} = start_node(cp1, Config), - ?line {ok, Cp2} = start_node(cp2, Config), - ?line {ok, Cp3} = start_node(cp3, Config), + {ok, Cp1} = start_node(cp1, Config), + {ok, Cp2} = start_node(cp2, Config), + {ok, Cp3} = start_node(cp3, Config), - test_server:sleep(1000), + ct:sleep(1000), - ?line {error, illegal_function_call} = + {error, illegal_function_call} = rpc:call(Cp1, global_group, registered_names_test, [{node, Cp1nn}]), - ?line {badarg,_} = + {badarg,_} = rpc:call(Cp1, global_group, send, [king, "The message"]), - ?line undefined = rpc:call(Cp1, global_group, whereis_name, [king]), - - % stop the nodes, and make sure names are released. + undefined = rpc:call(Cp1, global_group, whereis_name, [king]), + + % make sure the search process really exits after every global_group operations + ProcessCount0 = rpc:call(Cp1, erlang, system_info, [process_count]), + _ = rpc:call(Cp1, global_group, whereis_name, [{node, Cp1nn}, whatever_pid_name]), + ProcessCount1 = rpc:call(Cp1, erlang, system_info, [process_count]), + _ = rpc:call(Cp1, global_group, registered_names, [{node, Cp1nn}]), + ProcessCount2 = rpc:call(Cp1, erlang, system_info, [process_count]), + _ = rpc:call(Cp1, global_group, send, [{node, Cp1nn}, whatever_pid_name, msg]), + ProcessCount3 = rpc:call(Cp1, erlang, system_info, [process_count]), + ProcessCount0 = ProcessCount1 = ProcessCount2 = ProcessCount3, + + %% stop the nodes, and make sure names are released. stop_node(Cp1), stop_node(Cp2), stop_node(Cp3), - % sleep to let the nodes die - test_server:sleep(1000), + %% sleep to let the nodes die + ct:sleep(1000), - ?line test_server:timetrap_cancel(Dog), ok. - + start_node(Name, Config) -> Pa=filename:dirname(code:which(?MODULE)), - Dir=?config(priv_dir, Config), + Dir=proplists:get_value(priv_dir, Config), ConfFile = " -config " ++ filename:join(Dir, "global_group"), test_server:start_node(Name, slave, [{args, "-pa " ++ Pa ++ ConfFile}]). start_node_no(Name, Config) -> Pa=filename:dirname(code:which(?MODULE)), - Dir=?config(priv_dir, Config), + Dir=proplists:get_value(priv_dir, Config), ConfFile = " -config " ++ filename:join(Dir, "no_global_group"), test_server:start_node(Name, slave, [{args, "-pa " ++ Pa ++ ConfFile}]). start_node_no2(Name, Config) -> Pa=filename:dirname(code:which(?MODULE)), - Dir=?config(priv_dir, Config), + Dir=proplists:get_value(priv_dir, Config), ConfFile = " -config " ++ filename:join(Dir, "no_global_group_sync"), test_server:start_node(Name, slave, [{args, "-pa " ++ Pa ++ ConfFile}]). start_node_comp(Name, Config) -> Pa=filename:dirname(code:which(?MODULE)), - Dir=?config(priv_dir, Config), + Dir=proplists:get_value(priv_dir, Config), ConfFile = " -config " ++ filename:join(Dir, "global_group_comp"), test_server:start_node(Name, slave, [{args, "-pa " ++ Pa ++ ConfFile}]). @@ -1241,17 +1211,17 @@ node_name(Name, Config) -> lists:concat([Name,U,?testcase,U,Pid,U,U,L]). stop_node(Node) -> - ?t:stop_node(Node). + test_server:stop_node(Node). wait_for_ready_net() -> Nodes = lists:sort(?NODES), ?UNTIL(begin lists:all(fun(N) -> Nodes =:= get_known(N) end, Nodes) and - lists:all(fun(N) -> - LNs = rpc:call(N, erlang, nodes, []), - Nodes =:= lists:sort([N | LNs]) - end, Nodes) + lists:all(fun(N) -> + LNs = rpc:call(N, erlang, nodes, []), + Nodes =:= lists:sort([N | LNs]) + end, Nodes) end). get_known(Node) -> @@ -1261,11 +1231,11 @@ get_known(Node) -> config_hidden(Fd, Ncp1, Ncp2, Ncp3, Ncpx, Ncpy, Ncpz, Ncpq) -> M = from($@, atom_to_list(node())), io:format(Fd, "[{kernel, [{sync_nodes_optional, ['~s@~s','~s@~s','~s@~s', " - " '~s@~s','~s@~s','~s@~s']}," - "{sync_nodes_timeout, 1000}," - "{global_groups, [{nc1, hidden, ['~s@~s','~s@~s','~s@~s']}, " - "{nc2, hidden, ['~s@~s','~s@~s','~s@~s']}, " - "{nc3, normal, ['~s@~s']}]} ] }]. ~n", + " '~s@~s','~s@~s','~s@~s']}," + "{sync_nodes_timeout, 1000}," + "{global_groups, [{nc1, hidden, ['~s@~s','~s@~s','~s@~s']}, " + "{nc2, hidden, ['~s@~s','~s@~s','~s@~s']}, " + "{nc3, normal, ['~s@~s']}]} ] }]. ~n", [Ncp1, M, Ncp2, M, Ncp3, M, Ncpx, M, Ncpy, M, Ncpz, M, Ncp1, M, Ncp2, M, Ncp3, M, @@ -1275,11 +1245,11 @@ config_hidden(Fd, Ncp1, Ncp2, Ncp3, Ncpx, Ncpy, Ncpz, Ncpq) -> config(Fd, Ncp1, Ncp2, Ncp3, Ncpx, Ncpy, Ncpz, Ncpq) -> M = from($@, atom_to_list(node())), io:format(Fd, "[{kernel, [{sync_nodes_optional, ['~s@~s','~s@~s','~s@~s', " - " '~s@~s','~s@~s','~s@~s']}," - "{sync_nodes_timeout, 1000}," - "{global_groups, [{nc1, ['~s@~s','~s@~s','~s@~s']}, " - " {nc2, ['~s@~s','~s@~s','~s@~s']}, " - "{nc3, ['~s@~s']}]} ] }]. ~n", + " '~s@~s','~s@~s','~s@~s']}," + "{sync_nodes_timeout, 1000}," + "{global_groups, [{nc1, ['~s@~s','~s@~s','~s@~s']}, " + " {nc2, ['~s@~s','~s@~s','~s@~s']}, " + "{nc3, ['~s@~s']}]} ] }]. ~n", [Ncp1, M, Ncp2, M, Ncp3, M, Ncpx, M, Ncpy, M, Ncpz, M, Ncp1, M, Ncp2, M, Ncp3, M, @@ -1292,9 +1262,9 @@ config_no(Fd) -> config_sync(Fd, Ncp1, Ncp2, Ncp3, Ncpx, Ncpy, Ncpz) -> M = from($@, atom_to_list(node())), io:format(Fd, "[{kernel, [{sync_nodes_optional, ['~s@~s','~s@~s','~s@~s', " - " '~s@~s','~s@~s','~s@~s']}," - "{sync_nodes_timeout, 1000}," - "{global_groups, []} ] }] .~n", + " '~s@~s','~s@~s','~s@~s']}," + "{sync_nodes_timeout, 1000}," + "{global_groups, []} ] }] .~n", [Ncp1, M, Ncp2, M, Ncp3, M, Ncpx, M, Ncpy, M, Ncpz, M]). @@ -1302,8 +1272,8 @@ config_sync(Fd, Ncp1, Ncp2, Ncp3, Ncpx, Ncpy, Ncpz) -> config_comp(Fd, Ncp1, Ncp2, Ncp3, Ncpx, Ncpy, Ncpz) -> M = from($@, atom_to_list(node())), io:format(Fd, "[{kernel, [{sync_nodes_optional, ['~s@~s','~s@~s','~s@~s', " - " '~s@~s','~s@~s','~s@~s']}," - "{sync_nodes_timeout, 1000} ] }] .~n", + " '~s@~s','~s@~s','~s@~s']}," + "{sync_nodes_timeout, 1000} ] }] .~n", [Ncp1, M, Ncp2, M, Ncp3, M, Ncpx, M, Ncpy, M, Ncpz, M]). @@ -1324,7 +1294,7 @@ start_proc(Name) -> receive {Pid, Res} -> {Pid, Res} end. - + start_proc_rereg(Name) -> Pid = spawn(?MODULE, init2, [self(), Name]), receive @@ -1436,9 +1406,9 @@ assert_loop(Cp, CpName, Name, NamePid, Loop) -> Loop -> ok; Other1 -> - test_server:fail(Other1) + ct:fail(Other1) after 5000 -> - test_server:fail(timeout) + ct:fail(timeout) end. loop_until_true(Fun) -> diff --git a/lib/kernel/test/heart_SUITE.erl b/lib/kernel/test/heart_SUITE.erl index 35d3b75b34..f5ca6d0e1d 100644 --- a/lib/kernel/test/heart_SUITE.erl +++ b/lib/kernel/test/heart_SUITE.erl @@ -1,24 +1,25 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2014. All Rights Reserved. +%% Copyright Ericsson AB 1996-2018. 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/. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at %% -%% 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. +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% -module(heart_SUITE). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, start/1, restart/1, @@ -26,46 +27,55 @@ node_start_immediately_after_crash/1, node_start_soon_after_crash/1, set_cmd/1, clear_cmd/1, get_cmd/1, - dont_drop/1, kill_pid/1]). + callback_api/1, + options_api/1, + dont_drop/1, kill_pid/1, heart_no_kill/1]). -export([init_per_testcase/2, end_per_testcase/2]). --export([start_heart_stress/1, mangle/1, suicide_by_heart/0]). +-export([start_heart_stress/1, mangle/1, suicide_by_heart/0, non_suicide_by_heart/0]). -define(DEFAULT_TIMEOUT_SECS, 120). +-define(UNIQ_NODE_NAME, + list_to_atom(?MODULE_STRING ++ "__" ++ + atom_to_list(?FUNCTION_NAME) ++ "_" ++ + integer_to_list(erlang:unique_integer([positive])))). + init_per_testcase(_Func, Config) -> - Dog=test_server:timetrap(test_server:seconds(?DEFAULT_TIMEOUT_SECS)), - [{watchdog, Dog}|Config]. + Config. -end_per_testcase(_Func, Config) -> +end_per_testcase(_Func, _Config) -> Nodes = nodes(), lists:foreach(fun(X) -> NNam = list_to_atom(hd(string:tokens(atom_to_list(X),"@"))), case NNam of heart_test -> - ?t:format(1, "WARNING: Killed ~p~n", [X]), + ct:pal(?HI_VERBOSITY, "WARNING: Killed ~p~n", [X]), rpc:cast(X, erlang, halt, []); _ -> ok end - end, Nodes), - Dog=?config(watchdog, Config), - test_server:timetrap_cancel(Dog). + end, Nodes). %%----------------------------------------------------------------- %% Test suite for heart. %% Should be started in a CC view with: %% erl -sname master -rsh ctrsh %%----------------------------------------------------------------- -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap,{minutes,2}}]. all() -> [ start, restart, reboot, node_start_immediately_after_crash, node_start_soon_after_crash, set_cmd, clear_cmd, get_cmd, - kill_pid + callback_api, + options_api, + kill_pid, + heart_no_kill ]. groups() -> @@ -92,7 +102,7 @@ end_per_suite(Config) when is_list(Config) -> start_check(Type, Name) -> start_check(Type, Name, []). start_check(Type, Name, Envs) -> - Args = case ?t:os_type() of + Args = case test_server:os_type() of {win32,_} -> "+t50000 -heart " ++ env_encode([{"HEART_COMMAND", no_reboot}|Envs]); _ -> @@ -102,32 +112,30 @@ start_check(Type, Name, Envs) -> loose -> loose_node:start(Name, Args, ?DEFAULT_TIMEOUT_SECS); _ -> - ?t:start_node(Name, Type, [{args, Args}]) + test_server:start_node(Name, Type, [{args, Args}]) end, erlang:monitor_node(Node, true), case rpc:call(Node, erlang, whereis, [heart]) of Pid when is_pid(Pid) -> ok; _ -> - test_server:fail(heart_not_started) + ct:fail(heart_not_started) end, {ok, Node}. -start(doc) -> []; -start(suite) -> {req, [{time, 10}]}; start(Config) when is_list(Config) -> - {ok, Node} = start_check(slave, heart_test), + {ok, Node} = start_check(slave, ?UNIQ_NODE_NAME), rpc:call(Node, init, reboot, []), receive {nodedown, Node} -> ok - after 2000 -> test_server:fail(node_not_closed) + after 2000 -> ct:fail(node_not_closed) end, - test_server:sleep(5000), + timer:sleep(5000), case net_adm:ping(Node) of pang -> ok; _ -> - test_server:fail(node_rebooted) + ct:fail(node_rebooted) end, test_server:stop_node(Node). @@ -140,46 +148,36 @@ start(Config) when is_list(Config) -> %% restart %% Purpose: %% Check that a node is up and running after a init:restart/0 -restart(doc) -> []; -restart(suite) -> - case ?t:os_type() of - {Fam, _} when Fam == unix; Fam == win32 -> - {req, [{time,10}]}; - _ -> - {skip, "Only run on unix and win32"} - end; restart(Config) when is_list(Config) -> - {ok, Node} = start_check(loose, heart_test), + {ok, Node} = start_check(loose, ?UNIQ_NODE_NAME), rpc:call(Node, init, restart, []), receive {nodedown, Node} -> ok - after 2000 -> - test_server:fail(node_not_closed) + after 5000 -> + ct:fail(node_not_closed) end, - test_server:sleep(5000), + timer:sleep(5000), node_check_up_down(Node, 2000), loose_node:stop(Node). %% reboot %% Purpose: %% Check that a node is up and running after a init:reboot/0 -reboot(doc) -> []; -reboot(suite) -> {req, [{time, 10}]}; reboot(Config) when is_list(Config) -> - {ok, Node} = start_check(slave, heart_test), + {ok, Node} = start_check(slave, ?UNIQ_NODE_NAME), ok = rpc:call(Node, heart, set_cmd, - [atom_to_list(lib:progname()) ++ + [ct:get_progname() ++ " -noshell -heart " ++ name(Node) ++ "&"]), rpc:call(Node, init, reboot, []), receive {nodedown, Node} -> ok after 2000 -> - test_server:fail(node_not_closed) + ct:fail(node_not_closed) end, - test_server:sleep(5000), + timer:sleep(5000), node_check_up_down(Node, 2000), ok. @@ -191,7 +189,6 @@ reboot(Config) when is_list(Config) -> %% May currently dump core in beam debug build due to lock-order violation %% This should be removed when a non-lockad information retriever is implemented %% for crash dumps -node_start_immediately_after_crash(suite) -> {req, [{time, 10}]}; node_start_immediately_after_crash(Config) when is_list(Config) -> Config2 = ignore_cores:setup(?MODULE, node_start_immediately_after_crash, Config, true), try @@ -202,10 +199,11 @@ node_start_immediately_after_crash(Config) when is_list(Config) -> node_start_immediately_after_crash_test(Config) when is_list(Config) -> - {ok, Node} = start_check(loose, heart_test_imm, [{"ERL_CRASH_DUMP_SECONDS", "0"}]), + {ok, Node} = start_check(loose, ?UNIQ_NODE_NAME, + [{"ERL_CRASH_DUMP_SECONDS", "0"}]), ok = rpc:call(Node, heart, set_cmd, - [atom_to_list(lib:progname()) ++ + [ct:get_progname() ++ " -noshell -heart " ++ name(Node) ++ "&"]), Mod = exhaust_atoms, @@ -224,13 +222,13 @@ node_start_immediately_after_crash_test(Config) when is_list(Config) -> T0 = now(), receive {nodedown, Node} -> - test_server:format("Took ~.2f s. for node to go down~n", [timer:now_diff(now(), T0)/1000000]), + io:format("Took ~.2f s. for node to go down~n", [timer:now_diff(now(), T0)/1000000]), ok %% timeout is very liberal here. nodedown is received in about 1 s. on linux (palantir) %% and in about 10 s. on solaris (carcharoth) - after (15000*test_server:timetrap_scale_factor()) -> test_server:fail(node_not_closed) + after (15000*test_server:timetrap_scale_factor()) -> ct:fail(node_not_closed) end, - test_server:sleep(3000), + timer:sleep(3000), node_check_up_down(Node, 2000), loose_node:stop(Node). @@ -243,7 +241,6 @@ node_start_immediately_after_crash_test(Config) when is_list(Config) -> %% May currently dump core in beam debug build due to lock-order violation %% This should be removed when a non-lockad information retriever is implemented %% for crash dumps -node_start_soon_after_crash(suite) -> {req, [{time, 10}]}; node_start_soon_after_crash(Config) when is_list(Config) -> Config2 = ignore_cores:setup(?MODULE, node_start_soon_after_crash, Config, true), try @@ -253,10 +250,11 @@ node_start_soon_after_crash(Config) when is_list(Config) -> end. node_start_soon_after_crash_test(Config) when is_list(Config) -> - {ok, Node} = start_check(loose, heart_test_soon, [{"ERL_CRASH_DUMP_SECONDS", "10"}]), + {ok, Node} = start_check(loose, ?UNIQ_NODE_NAME, + [{"ERL_CRASH_DUMP_SECONDS", "10"}]), ok = rpc:call(Node, heart, set_cmd, - [atom_to_list(lib:progname()) ++ + [ct:get_progname() ++ " -noshell -heart " ++ name(Node) ++ "&"]), Mod = exhaust_atoms, @@ -273,9 +271,9 @@ node_start_soon_after_crash_test(Config) when is_list(Config) -> rpc:cast(Node, Mod, do, []), receive {nodedown, Node} -> ok - after (15000*test_server:timetrap_scale_factor()) -> test_server:fail(node_not_closed) + after (15000*test_server:timetrap_scale_factor()) -> ct:fail(node_not_closed) end, - test_server:sleep(20000), + timer:sleep(20000), node_check_up_down(Node, 15000), loose_node:stop(Node). @@ -288,16 +286,15 @@ node_check_up_down(Node, Tmo) -> receive {nodedown, Node} -> ok after Tmo -> - test_server:fail(node_not_closed2) + ct:fail(node_not_closed2) end; _ -> - test_server:fail(node_not_rebooted) + ct:fail(node_not_rebooted) end. %% Only tests bad command, correct behaviour is tested in reboot/1. -set_cmd(suite) -> []; set_cmd(Config) when is_list(Config) -> - {ok, Node} = start_check(slave, heart_test), + {ok, Node} = start_check(slave, ?UNIQ_NODE_NAME), Cmd = wrong_atom, {error, {bad_cmd, Cmd}} = rpc:call(Node, heart, set_cmd, [Cmd]), Cmd1 = lists:duplicate(2047, $a), @@ -309,25 +306,24 @@ set_cmd(Config) when is_list(Config) -> stop_node(Node), ok. -clear_cmd(suite) -> {req,[{time,15}]}; clear_cmd(Config) when is_list(Config) -> - {ok, Node} = start_check(slave, heart_test), + {ok, Node} = start_check(slave, ?UNIQ_NODE_NAME), ok = rpc:call(Node, heart, set_cmd, - [atom_to_list(lib:progname()) ++ + [ct:get_progname() ++ " -noshell -heart " ++ name(Node) ++ "&"]), rpc:call(Node, init, reboot, []), receive {nodedown, Node} -> ok after 2000 -> - test_server:fail(node_not_closed) + ct:fail(node_not_closed) end, - test_server:sleep(5000), + timer:sleep(5000), case net_adm:ping(Node) of pong -> erlang:monitor_node(Node, true); _ -> - test_server:fail(node_not_rebooted) + ct:fail(node_not_rebooted) end, ok = rpc:call(Node, heart, set_cmd, ["erl -noshell -heart " ++ name(Node) ++ "&"]), @@ -337,35 +333,102 @@ clear_cmd(Config) when is_list(Config) -> {nodedown, Node} -> ok after 2000 -> - test_server:fail(node_not_closed) + ct:fail(node_not_closed) end, - test_server:sleep(5000), + timer:sleep(5000), case net_adm:ping(Node) of pang -> ok; _ -> - test_server:fail(node_rebooted) + ct:fail(node_rebooted) end, ok. -get_cmd(suite) -> []; get_cmd(Config) when is_list(Config) -> - {ok, Node} = start_check(slave, heart_test), - Cmd = "test", - ok = rpc:call(Node, heart, set_cmd, [Cmd]), - {ok, Cmd} = rpc:call(Node, heart, get_cmd, []), + {ok, Node} = start_check(slave, ?UNIQ_NODE_NAME), + + ShortCmd = "test", + ok = rpc:call(Node, heart, set_cmd, [ShortCmd]), + {ok, ShortCmd} = rpc:call(Node, heart, get_cmd, []), + + %% This would hang prior to OTP-15024 being fixed. + LongCmd = [$a || _ <- lists:seq(1, 160)], + ok = rpc:call(Node, heart, set_cmd, [LongCmd]), + {ok, LongCmd} = rpc:call(Node, heart, get_cmd, []), + + stop_node(Node), + ok. + +callback_api(Config) when is_list(Config) -> + {ok, Node} = start_check(slave, ?UNIQ_NODE_NAME), + none = rpc:call(Node, heart, get_callback, []), + M0 = self(), + F0 = ok, + {error, {bad_callback, {M0,F0}}} = rpc:call(Node, heart, set_callback, [M0,F0]), + none = rpc:call(Node, heart, get_callback, []), + M1 = lists:duplicate(28, $a), + F1 = lists:duplicate(28, $b), + {error, {bad_callback, {M1,F1}}} = rpc:call(Node, heart, set_callback, [M1,F1]), + none = rpc:call(Node, heart, get_callback, []), + + M2 = heart_check_module, + F2 = cb_ok, + F3 = cb_error, + Code0 = generate(M2, [], [ + atom_to_list(F2) ++ "() -> ok.", + atom_to_list(F3) ++ "() -> exit(\"callback_error (as intended)\")." + ]), + {module, M2} = rpc:call(Node, erlang, load_module, [M2, Code0]), + ok = rpc:call(Node, M2, F2, []), + ok = rpc:call(Node, heart, set_callback, [M2,F2]), + {ok, {M2,F2}} = rpc:call(Node, heart, get_callback, []), + ok = rpc:call(Node, heart, clear_callback, []), + none = rpc:call(Node, heart, get_callback, []), + ok = rpc:call(Node, heart, set_callback, [M2,F2]), + {ok, {M2,F2}} = rpc:call(Node, heart, get_callback, []), + ok = rpc:call(Node, heart, set_callback, [M2,F3]), + receive {nodedown, Node} -> ok + after 5000 -> ct:fail(node_not_killed) + end, stop_node(Node), ok. -dont_drop(suite) -> +options_api(Config) when is_list(Config) -> + {ok, Node} = start_check(slave, ?UNIQ_NODE_NAME), + none = rpc:call(Node, heart, get_options, []), + M0 = self(), + F0 = ok, + {error, {bad_options, {M0,F0}}} = rpc:call(Node, heart, set_options, [{M0,F0}]), + none = rpc:call(Node, heart, get_options, []), + Ls = lists:duplicate(28, $b), + {error, {bad_options, Ls}} = rpc:call(Node, heart, set_options, [Ls]), + none = rpc:call(Node, heart, get_options, []), + + ok = rpc:call(Node, heart, set_options, [[check_schedulers]]), + {ok, [check_schedulers]} = rpc:call(Node, heart, get_options, []), + ok = rpc:call(Node, heart, set_options, [[]]), + none = rpc:call(Node, heart, get_options, []), + + ok = rpc:call(Node, heart, set_options, [[check_schedulers]]), + {ok, [check_schedulers]} = rpc:call(Node, heart, get_options, []), + {error, {bad_options, Ls}} = rpc:call(Node, heart, set_options, [Ls]), + {ok, [check_schedulers]} = rpc:call(Node, heart, get_options, []), + + receive after 3000 -> ok end, %% wait 3 secs + + ok = rpc:call(Node, heart, set_options, [[]]), + none = rpc:call(Node, heart, get_options, []), + stop_node(Node), + ok. + + %%% Removed as it may crash epmd/distribution in colourful %%% ways. While we ARE finding out WHY, it would %%% be nice for others to be able to run the kernel test suite -%%% without "exploding machines", so thats why I removed it for now. - []; -dont_drop(doc) -> - ["Tests that the heart command does not get dropped when ", - "set just before halt on very high I/O load."]; +%%% without "exploding machines", so that's why I removed it for now. + +%% Tests that the heart command does not get dropped when +%% set just before halt on very high I/O load.. dont_drop(Config) when is_list(Config) -> %%% Have to do it some times to make it happen... [ok,ok,ok,ok,ok,ok,ok,ok,ok,ok] = do_dont_drop(Config,10), @@ -387,7 +450,7 @@ do_dont_drop(Config,N) -> Env = [{"HEART_COMMAND", FirstCmd}], Func = "start_heart_stress", Arg = NN3 ++ "@" ++ Host ++ " " ++ - filename:join(?config(data_dir, Config), "simple_echo"), + filename:join(proplists:get_value(data_dir, Config), "simple_echo"), start_node_run(Name,Env,Func,Arg), case wait_for_any_of(list_to_atom(NN2 ++ "@" ++ Host), list_to_atom(NN3 ++ "@" ++ Host)) of @@ -420,16 +483,13 @@ wait_for_any_of(N1,N2,Times) -> end. -kill_pid(suite) -> - []; -kill_pid(doc) -> - ["Tests that heart kills the old erlang node before executing ", - "heart command."]; +%% Tests that heart kills the old erlang node before executing +%% heart command. kill_pid(Config) when is_list(Config) -> ok = do_kill_pid(Config). do_kill_pid(_Config) -> - Name = heart_test, + Name = ?UNIQ_NODE_NAME, Env = [{"HEART_COMMAND", "nickeNyfikenFarEttJobb"}], {ok,Node} = start_node_run(Name,Env,suicide_by_heart,[]), ok = wait_for_node(Node,15), @@ -439,6 +499,30 @@ do_kill_pid(_Config) -> false end. + +heart_no_kill(suite) -> + []; +heart_no_kill(doc) -> + ["Tests that heart doesn't kill the old erlang node when ", + "HEART_NO_KILL is set."]; +heart_no_kill(Config) when is_list(Config) -> + ok = do_no_kill(Config). + +do_no_kill(_Config) -> + Name = heart_test, + {ok,Node} = start_node_run(Name,[],non_suicide_by_heart,[]), + io:format("Node is ~p~n", [Node]), + ok = wait_for_node(Node,15), + io:format("wait_for_node is ~p~n", [ok]), + erlang:monitor_node(Node, true), + receive {nodedown,Node} -> false + after 30000 -> + io:format("Node didn't die..\n"), + rpc:call(Node,init,stop,[]), + io:format("done init:stop..\n"), + ok + end. + wait_for_node(_,0) -> false; wait_for_node(Node,N) -> @@ -557,18 +641,34 @@ suicide_by_heart() -> sallad end. +non_suicide_by_heart() -> + P = open_port({spawn,"heart -ht 11 -pid "++os:getpid()}, + [exit_status, {env, [{"HEART_NO_KILL", "TRUE"}]}, + {packet,2}]), + receive X -> X end, + %% Just hang and wait for heart to timeout + receive + {P,{exit_status,_}} -> + ok + after + 20000 -> + exit(timeout) + end. + %% generate a module from binary generate(Module, Attributes, FunStrings) -> FunForms = function_forms(FunStrings), Forms = [ - {attribute,1,module,Module}, - {attribute,2,export,[FA || {FA,_} <- FunForms]} - ] ++ [{attribute, 3, A, V}|| {A, V} <- Attributes] ++ + {attribute,a(1),module,Module}, + {attribute,a(2),export,[FA || {FA,_} <- FunForms]} + ] ++ [{attribute, a(3), A, V}|| {A, V} <- Attributes] ++ [ Function || {_, Function} <- FunForms], {ok, Module, Bin} = compile:forms(Forms), Bin. +a(L) -> + erl_anno:new(L). function_forms([]) -> []; function_forms([S|Ss]) -> diff --git a/lib/kernel/test/ignore_cores.erl b/lib/kernel/test/ignore_cores.erl index d4bb02df3f..fde65bf5c4 100644 --- a/lib/kernel/test/ignore_cores.erl +++ b/lib/kernel/test/ignore_cores.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2013. All Rights Reserved. +%% Copyright Ericsson AB 2008-2016. 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/. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at %% -%% 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. +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% @@ -27,7 +28,7 @@ -module(ignore_cores). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -export([init/1, fini/1, setup/3, setup/4, restore/1, dir/1]). @@ -52,7 +53,7 @@ init(Config) -> fini(Config) -> #ignore_cores{org_cwd = OrgCWD, org_path = OrgPath, - org_pwd_env = OrgPWD} = ?config(ignore_cores, Config), + org_pwd_env = OrgPWD} = proplists:get_value(ignore_cores, Config), ok = file:set_cwd(OrgCWD), true = code:set_path(OrgPath), case OrgPWD of @@ -69,10 +70,10 @@ setup(Suite, Testcase, Config, SetCwd) when is_atom(Suite), is_list(Config) -> #ignore_cores{org_cwd = OrgCWD, org_path = OrgPath, - org_pwd_env = OrgPWD} = ?config(ignore_cores, Config), + org_pwd_env = OrgPWD} = proplists:get_value(ignore_cores, Config), Path = lists:map(fun (".") -> OrgCWD; (Dir) -> Dir end, OrgPath), true = code:set_path(Path), - PrivDir = ?config(priv_dir, Config), + PrivDir = proplists:get_value(priv_dir, Config), IgnDir = filename:join([PrivDir, atom_to_list(Suite) ++ "_" @@ -93,7 +94,7 @@ setup(Suite, Testcase, Config, SetCwd) when is_atom(Suite), end, ok = file:write_file(filename:join([IgnDir, "ignore_core_files"]), <<>>), %% cores are dumped in /cores on MacOS X - CoresDir = case {?t:os_type(), filelib:is_dir("/cores")} of + CoresDir = case {test_server:os_type(), filelib:is_dir("/cores")} of {{unix,darwin}, true} -> filelib:fold_files("/cores", "^core.*$", @@ -118,7 +119,7 @@ restore(Config) -> org_path = OrgPath, org_pwd_env = OrgPWD, ign_dir = IgnDir, - cores_dir = CoresDir} = ?config(ignore_cores, Config), + cores_dir = CoresDir} = proplists:get_value(ignore_cores, Config), try case CoresDir of false -> @@ -154,5 +155,5 @@ restore(Config) -> dir(Config) -> - #ignore_cores{ign_dir = Dir} = ?config(ignore_cores, Config), + #ignore_cores{ign_dir = Dir} = proplists:get_value(ignore_cores, Config), Dir. diff --git a/lib/kernel/test/inet_SUITE.erl b/lib/kernel/test/inet_SUITE.erl index 44a32fc1ec..2e5f8c7d2c 100644 --- a/lib/kernel/test/inet_SUITE.erl +++ b/lib/kernel/test/inet_SUITE.erl @@ -1,24 +1,25 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2015. All Rights Reserved. +%% Copyright Ericsson AB 1997-2018. 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/. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at %% -%% 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. +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% -module(inet_SUITE). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -include_lib("kernel/include/inet.hrl"). -include_lib("kernel/src/inet_dns.hrl"). @@ -39,13 +40,17 @@ lookup_bad_search_option/1, getif/1, getif_ifr_name_overflow/1,getservbyname_overflow/1, getifaddrs/1, - parse_strict_address/1, simple_netns/1, simple_netns_open/1]). + parse_strict_address/1, ipv4_mapped_ipv6_address/1, + simple_netns/1, simple_netns_open/1, + simple_bind_to_device/1, simple_bind_to_device_open/1]). -export([get_hosts/1, get_ipv6_hosts/1, parse_hosts/1, parse_address/1, kill_gethost/0, parallell_gethost/0, test_netns/0]). -export([init_per_testcase/2, end_per_testcase/2]). -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap,{minutes,1}}]. all() -> [t_gethostbyaddr, t_gethostbyname, t_getaddr, @@ -55,7 +60,8 @@ all() -> gethostnative_debug_level, gethostnative_soft_restart, lookup_bad_search_option, getif, getif_ifr_name_overflow, getservbyname_overflow, - getifaddrs, parse_strict_address, simple_netns, simple_netns_open]. + getifaddrs, parse_strict_address, simple_netns, simple_netns_open, + simple_bind_to_device, simple_bind_to_device_open]. groups() -> [{parse, [], [parse_hosts, parse_address]}]. @@ -96,136 +102,133 @@ init_per_testcase(lookup_bad_search_option, Config) -> Prev = ets:lookup(Db, Key), ets:delete(Db, Key), ets:insert(Db, {Key,[lookup_bad_search_option]}), - ?t:format("Misconfigured resolver lookup order", []), - Dog = test_server:timetrap(test_server:seconds(60)), - [{Key,Prev},{watchdog,Dog}|Config]; + io:format("Misconfigured resolver lookup order", []), + [{Key,Prev}|Config]; init_per_testcase(_Func, Config) -> - Dog = test_server:timetrap(test_server:seconds(60)), - [{watchdog,Dog}|Config]. + Config. end_per_testcase(lookup_bad_search_option, Config) -> - Dog = ?config(watchdog, Config), - test_server:timetrap_cancel(Dog), Db = inet_db, Key = res_lookup, - Prev = ?config(Key, Config), + Prev = proplists:get_value(Key, Config), ets:delete(Db, Key), ets:insert(Db, Prev), - ?t:format("Restored resolver lookup order", []); -end_per_testcase(_Func, Config) -> - Dog = ?config(watchdog, Config), - test_server:timetrap_cancel(Dog). + io:format("Restored resolver lookup order", []); +end_per_testcase(_Func, _Config) -> + ok. t_gethostbyaddr() -> required(v4). -t_gethostbyaddr(doc) -> "Test the inet:gethostbyaddr/1 function."; +%% Test the inet:gethostbyaddr/1 function. t_gethostbyaddr(Config) when is_list(Config) -> - ?line {Name,FullName,IPStr,{A,B,C,D}=IP,Aliases,_,_} = - ct:get_config(test_host_ipv4_only), - ?line Rname = integer_to_list(D) ++ "." ++ + {Name,FullName,IPStr,{A,B,C,D}=IP,Aliases,_,_} = ct:get_config(test_host_ipv4_only), + Rname = integer_to_list(D) ++ "." ++ integer_to_list(C) ++ "." ++ integer_to_list(B) ++ "." ++ integer_to_list(A) ++ ".in-addr.arpa", - ?line {ok,HEnt} = inet:gethostbyaddr(IPStr), - ?line {ok,HEnt} = inet:gethostbyaddr(IP), - ?line {error,Error} = inet:gethostbyaddr(Name), - ?line ok = io:format("Failure reason: ~p: ~s", - [error,inet:format_error(Error)]), - ?line HEnt_ = HEnt#hostent{h_addrtype = inet, - h_length = 4, - h_addr_list = [IP]}, - ?line HEnt_ = HEnt, + {ok,HEnt} = inet:gethostbyaddr(IPStr), + {ok,HEnt} = inet:gethostbyaddr(IP), + {error,Error} = inet:gethostbyaddr(Name), + ok = io:format("Failure reason: ~p: ~s", [error,inet:format_error(Error)]), + HEnt_ = HEnt#hostent{h_addrtype = inet, + h_length = 4, + h_addr_list = [IP]}, + HEnt_ = HEnt, case {os:type(),os:version()} of - {{unix,freebsd},{5,0,0}} -> - %% The alias list seems to be buggy in FreeBSD 5.0.0. - ?line check_elems([{HEnt#hostent.h_name,[Name,FullName]}]), - io:format("Buggy alias list: ~p", [HEnt#hostent.h_aliases]), - ok; - _ -> - ?line check_elems([{HEnt#hostent.h_name,[Name,FullName]}, - {HEnt#hostent.h_aliases,[[],Aliases,[Rname]]}]) + {{unix,freebsd},{5,0,0}} -> + %% The alias list seems to be buggy in FreeBSD 5.0.0. + check_elems([{HEnt#hostent.h_name,[Name,FullName]}]), + io:format("Buggy alias list: ~p", [HEnt#hostent.h_aliases]), + ok; + _ -> + io:format("alias list: ~p", [HEnt#hostent.h_aliases]), + io:format( + "check alias list: ~p", [[Aliases,tl(Aliases),[Rname]]]), + io:format("name: ~p", [HEnt#hostent.h_name]), + io:format("check name: ~p", [[Name,FullName]]), + check_elems( + [{HEnt#hostent.h_name,[Name,FullName]}, + {HEnt#hostent.h_aliases,[[],Aliases,tl(Aliases),[Rname]]}]) end, - ?line {_DName, _DFullName, DIPStr, DIP, _, _, _} = - ct:get_config(test_dummy_host), - ?line {error,nxdomain} = inet:gethostbyaddr(DIPStr), - ?line {error,nxdomain} = inet:gethostbyaddr(DIP), + {_DName, _DFullName, DIPStr, DIP, _, _, _} = ct:get_config(test_dummy_host), + {error,nxdomain} = inet:gethostbyaddr(DIPStr), + {error,nxdomain} = inet:gethostbyaddr(DIP), ok. t_gethostbyaddr_v6() -> required(v6). -t_gethostbyaddr_v6(doc) -> "Test the inet:gethostbyaddr/1 inet6 function."; +%% Test the inet:gethostbyaddr/1 inet6 function. t_gethostbyaddr_v6(Config) when is_list(Config) -> - ?line {Name6, FullName6, IPStr6, IP6, Aliases6} = + {Name6, FullName6, IPStr6, IP6, Aliases6} = ct:get_config(test_host_ipv6_only), - ?line case inet:gethostbyaddr(IPStr6) of + case inet:gethostbyaddr(IPStr6) of %% Even if IPv6 is not supported, the native resolver may succeed %% looking up the host. DNS lookup will probably fail. {error,nxdomain} -> {skip, "IPv6 test fails! IPv6 not supported on this host!?"}; {ok,HEnt6} -> - ?line {ok,HEnt6} = inet:gethostbyaddr(IP6), - ?line {error,Error6} = inet:gethostbyaddr(Name6), - ?line ok = io:format("Failure reason: ~p: ~s", - [Error6, inet:format_error(Error6)]), - ?line HEnt6_ = HEnt6#hostent{h_addrtype = inet6, - h_length = 16, - h_addr_list = [IP6]}, - ?line HEnt6_ = HEnt6, - ?line check_elems([{HEnt6#hostent.h_name,[Name6,FullName6]}, - {HEnt6#hostent.h_aliases,[[],Aliases6]}]), - - ?line {_DName6, _DFullName6, DIPStr6, DIP6, _} = - ct:get_config(test_dummy_ipv6_host), - ?line {error,nxdomain} = inet:gethostbyaddr(DIPStr6), - ?line {error,nxdomain} = inet:gethostbyaddr(DIP6), + {ok,HEnt6} = inet:gethostbyaddr(IP6), + {error,Error6} = inet:gethostbyaddr(Name6), + ok = io:format("Failure reason: ~p: ~s", + [Error6, inet:format_error(Error6)]), + HEnt6_ = HEnt6#hostent{h_addrtype = inet6, + h_length = 16, + h_addr_list = [IP6]}, + HEnt6_ = HEnt6, + check_elems( + [{HEnt6#hostent.h_name,[Name6,FullName6]}, + {HEnt6#hostent.h_aliases,[[],Aliases6,tl(Aliases6)]}]), + + {_DName6, _DFullName6, DIPStr6, DIP6, _} = + ct:get_config(test_dummy_ipv6_host), + {error,nxdomain} = inet:gethostbyaddr(DIPStr6), + {error,nxdomain} = inet:gethostbyaddr(DIP6), ok end. t_gethostbyname() -> required(v4). -t_gethostbyname(doc) -> "Test the inet:gethostbyname/1 function."; -t_gethostbyname(suite) -> []; +%% Test the inet:gethostbyname/1 function. t_gethostbyname(Config) when is_list(Config) -> - ?line {Name,FullName,IPStr,IP,Aliases,IP_46_Str,_} = + {Name,FullName,IPStr,IP,Aliases,IP_46_Str,_} = ct:get_config(test_host_ipv4_only), - ?line {ok,_} = inet:gethostbyname(IPStr), - ?line {ok,HEnt} = inet:gethostbyname(Name), - ?line {ok,HEnt} = inet:gethostbyname(list_to_atom(Name)), - ?line HEnt_ = HEnt#hostent{h_addrtype = inet, - h_length = 4, - h_addr_list = [IP]}, - - ?line HEnt_ = HEnt, - ?line check_elems([{HEnt#hostent.h_name,[Name,FullName]}, - {HEnt#hostent.h_aliases,[[],Aliases]}]), - ?line {ok,HEntF} = inet:gethostbyname(FullName), - ?line HEntF_ = HEntF#hostent{h_name = FullName, - h_addrtype = inet, - h_length = 4, - h_addr_list = [IP]}, - ?line HEntF_ = HEntF, - ?line check_elems([{HEnt#hostent.h_aliases,[[],Aliases]}]), + {ok,_} = inet:gethostbyname(IPStr), + {ok,HEnt} = inet:gethostbyname(Name), + {ok,HEnt} = inet:gethostbyname(list_to_atom(Name)), + HEnt_ = HEnt#hostent{h_addrtype = inet, + h_length = 4, + h_addr_list = [IP]}, + + HEnt_ = HEnt, + check_elems([{HEnt#hostent.h_name,[Name,FullName]}, + {HEnt#hostent.h_aliases,[[],Aliases,tl(Aliases)]}]), + {ok,HEntF} = inet:gethostbyname(FullName), + HEntF_ = HEntF#hostent{h_name = FullName, + h_addrtype = inet, + h_length = 4, + h_addr_list = [IP]}, + HEntF_ = HEntF, + check_elems([{HEnt#hostent.h_aliases,[[],Aliases,tl(Aliases)]}]), %% - ?line FullNameU = toupper(FullName), - ?line {ok,HEntU} = inet:gethostbyname(FullNameU), - ?line FullNameU = toupper(HEntU#hostent.h_name), - ?line #hostent{ + FullNameU = toupper(FullName), + {ok,HEntU} = inet:gethostbyname(FullNameU), + FullNameU = toupper(HEntU#hostent.h_name), + #hostent{ h_addrtype = inet, h_length = 4, h_addr_list = [IP]} = HEntU, - ?line check_elems( - [{[toupper(H) || H <- HEntU#hostent.h_aliases], - [[],[toupper(A) || A <- Aliases]]}]), + check_elems( + [{[toupper(H) || H <- HEntU#hostent.h_aliases], + [[],[toupper(A) || A <- Aliases]]}]), - ?line {DName, _DFullName, _DIPStr, _DIP, _, _, _} = + {DName, _DFullName, _DIPStr, _DIP, _, _, _} = ct:get_config(test_dummy_host), - ?line {error,nxdomain} = inet:gethostbyname(DName), - ?line {error,nxdomain} = inet:gethostbyname(IP_46_Str), + {error,nxdomain} = inet:gethostbyname(DName), + {error,nxdomain} = inet:gethostbyname(IP_46_Str), ok. t_gethostbyname_v6() -> required(v6). -t_gethostbyname_v6(doc) -> "Test the inet:gethostbyname/1 inet6 function."; -t_gethostbyname_v6(suite) -> []; +%% Test the inet:gethostbyname/1 inet6 function. t_gethostbyname_v6(Config) when is_list(Config) -> {Name, FullName, IPStr, IP, Aliases} = ct:get_config(test_host_ipv6_only), @@ -240,7 +243,7 @@ t_gethostbyname_v6(Config) when is_list(Config) -> h_length = 16} = HEnt, check_elems( [{HEnt#hostent.h_name,[Name,FullName]}, - {HEnt#hostent.h_aliases,[[],Aliases]}]); + {HEnt#hostent.h_aliases,[[],Aliases,tl(Aliases)]}]); [IP46] -> % IPv4 compatible address {ok,HEnt4} = inet:gethostbyname(Name, inet), #hostent{h_addrtype = inet, @@ -260,7 +263,7 @@ t_gethostbyname_v6(Config) when is_list(Config) -> h_addrtype = inet6, h_length = 16} = HEntF, check_elems( - [{HEnt#hostent.h_aliases,[[],Aliases]}]); + [{HEnt#hostent.h_aliases,[[],Aliases,tl(Aliases)]}]); [IP46F] -> % IPv4 compatible address {ok,HEnt4F} = inet:gethostbyname(FullName, inet), #hostent{h_addrtype = inet, @@ -285,33 +288,31 @@ check_elem(Val, [Val|_], _) -> ok; check_elem(Val, [_|Tests], Tests0) -> check_elem(Val, Tests, Tests0); check_elem(Val, [], Tests0) -> - ?t:fail({no_match,Val,Tests0}). + ct:fail({no_match,Val,Tests0}). t_getaddr() -> required(v4). -t_getaddr(doc) -> "Test the inet:getaddr/2 function."; -t_getaddr(suite) -> []; +%% Test the inet:getaddr/2 function. t_getaddr(Config) when is_list(Config) -> - ?line {Name,FullName,IPStr,IP,_,IP_46_Str,IP46} = + {Name,FullName,IPStr,IP,_,IP_46_Str,IP46} = ct:get_config(test_host_ipv4_only), - ?line {ok,IP} = inet:getaddr(list_to_atom(Name), inet), - ?line {ok,IP} = inet:getaddr(Name, inet), - ?line {ok,IP} = inet:getaddr(FullName, inet), - ?line {ok,IP} = inet:getaddr(IP, inet), - ?line {ok,IP} = inet:getaddr(IPStr, inet), - ?line {error,nxdomain} = inet:getaddr(IP_46_Str, inet), - ?line {error,eafnosupport} = inet:getaddr(IP46, inet), - - ?line {DName, DFullName, DIPStr, DIP, _, _, _} = ct:get_config(test_dummy_host), - ?line {error,nxdomain} = inet:getaddr(DName, inet), - ?line {error,nxdomain} = inet:getaddr(DFullName, inet), - ?line {ok,DIP} = inet:getaddr(DIPStr, inet), - ?line {ok,DIP} = inet:getaddr(DIP, inet), + {ok,IP} = inet:getaddr(list_to_atom(Name), inet), + {ok,IP} = inet:getaddr(Name, inet), + {ok,IP} = inet:getaddr(FullName, inet), + {ok,IP} = inet:getaddr(IP, inet), + {ok,IP} = inet:getaddr(IPStr, inet), + {error,nxdomain} = inet:getaddr(IP_46_Str, inet), + {error,eafnosupport} = inet:getaddr(IP46, inet), + + {DName, DFullName, DIPStr, DIP, _, _, _} = ct:get_config(test_dummy_host), + {error,nxdomain} = inet:getaddr(DName, inet), + {error,nxdomain} = inet:getaddr(DFullName, inet), + {ok,DIP} = inet:getaddr(DIPStr, inet), + {ok,DIP} = inet:getaddr(DIP, inet), ok. t_getaddr_v6() -> required(v4) ++ required(v6). -t_getaddr_v6(doc) -> "Test the inet:getaddr/2 function."; -t_getaddr_v6(suite) -> []; +%% Test the inet:getaddr/2 function. t_getaddr_v6(Config) when is_list(Config) -> {Name,FullName,IPStr,IP,_} = ct:get_config(test_host_ipv6_only), @@ -339,16 +340,15 @@ t_getaddr_v6(Config) when is_list(Config) -> end. ipv4_to_ipv6() -> required(v4). -ipv4_to_ipv6(doc) -> "Test if IPv4 address is converted to IPv6 address."; -ipv4_to_ipv6(suite) -> []; +%% Test if IPv4 address is converted to IPv6 address. ipv4_to_ipv6(Config) when is_list(Config) -> %% Test what happens if an IPv4 address is looked up in an IPv6 context. %% If the native resolver succeeds to look it up, an IPv4 compatible %% address should be returned. If no IPv6 support on this host, an %% error should beturned. - ?line {_Name,_FullName,IPStr,_IP,Aliases,IP_46_Str,IP_46} = + {_Name,_FullName,IPStr,_IP,Aliases,IP_46_Str,IP_46} = ct:get_config(test_host_ipv4_only), - ?line IP4to6Res = + IP4to6Res = case inet:getaddr(IPStr, inet6) of {ok,IP_46} -> io:format("IPv4->IPv6: success~n"), @@ -360,36 +360,34 @@ ipv4_to_ipv6(Config) when is_list(Config) -> io:format("IPv6->IPv4: eafnosupport~n"), E; Other -> - ?line ?t:fail({ipv4_to_ipv6_lookup_failed,Other}) + ct:fail({ipv4_to_ipv6_lookup_failed,Other}) end, - ?line case {IP4to6Res,inet:gethostbyname(IPStr, inet6)} of - {true,{ok,HEnt}} -> - ?line HEnt_ = HEnt#hostent{h_addrtype = inet6, - h_length = 16, - h_addr_list = [IP_46]}, - ?line HEnt_ = HEnt, - ?line check_elems([{HEnt#hostent.h_name,[IP_46_Str,IPStr]}, - {HEnt#hostent.h_aliases,[[],Aliases]}]); - {_,IP4to6Res} -> ok - end, + case {IP4to6Res,inet:gethostbyname(IPStr, inet6)} of + {true,{ok,HEnt}} -> + HEnt_ = HEnt#hostent{h_addrtype = inet6, + h_length = 16, + h_addr_list = [IP_46]}, + HEnt_ = HEnt, + check_elems([{HEnt#hostent.h_name,[IP_46_Str,IPStr]}, + {HEnt#hostent.h_aliases,[[],Aliases,tl(Aliases)]}]); + {_,IP4to6Res} -> ok + end, ok. -host_and_addr() -> required(hosts). -host_and_addr(doc) -> ["Test looking up hosts and addresses. Use 'ypcat hosts' ", - "or the local eqivalent to find all hosts."]; -host_and_addr(suite) -> []; -host_and_addr(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:minutes(5)), +host_and_addr() -> + [{timetrap,{minutes,5}}|required(hosts)]. - ?line lists:foreach(fun try_host/1, get_hosts(Config)), - ?line test_server:timetrap_cancel(Dog), +%% Test looking up hosts and addresses. Use 'ypcat hosts' +%% or the local eqivalent to find all hosts. +host_and_addr(Config) when is_list(Config) -> + lists:foreach(fun try_host/1, get_hosts(Config)), ok. try_host({Ip0, Host}) -> - ?line {ok,Ip} = inet:getaddr(Ip0, inet), - ?line {ok,{hostent, _, _, inet, _, Ips1}} = inet:gethostbyaddr(Ip), - ?line {ok,{hostent, _, _, inet, _, _Ips2}} = inet:gethostbyname(Host), - ?line true = lists:member(Ip, Ips1), + {ok,Ip} = inet:getaddr(Ip0, inet), + {ok,{hostent, _, _, inet, _, Ips1}} = inet:gethostbyaddr(Ip), + {ok,{hostent, _, _, inet, _, _Ips2}} = inet:gethostbyname(Host), + true = lists:member(Ip, Ips1), ok. %% Get all hosts from the system using 'ypcat hosts' or the local @@ -436,105 +434,139 @@ get_hosts([C|Rest], Cur, Ip, Result) -> get_hosts(Rest, [C|Cur], Ip, Result); get_hosts([], _, _, Result) -> Result. - + parse_hosts(Config) when is_list(Config) -> - ?line DataDir = ?config(data_dir,Config), - ?line HostFile = filename:join(DataDir, "hosts"), - ?line inet_parse:hosts(HostFile), - ?line HostFileErr1 = filename:join(DataDir, "hosts_err1"), - ?line inet_parse:hosts(HostFileErr1), - ?line Resolv = filename:join(DataDir,"resolv.conf"), - ?line inet_parse:resolv(Resolv), - ?line ResolvErr1 = filename:join(DataDir,"resolv.conf.err1"), - ?line inet_parse:resolv(ResolvErr1). + DataDir = proplists:get_value(data_dir,Config), + HostFile = filename:join(DataDir, "hosts"), + inet_parse:hosts(HostFile), + HostFileErr1 = filename:join(DataDir, "hosts_err1"), + inet_parse:hosts(HostFileErr1), + Resolv = filename:join(DataDir,"resolv.conf"), + inet_parse:resolv(Resolv), + ResolvErr1 = filename:join(DataDir,"resolv.conf.err1"), + inet_parse:resolv(ResolvErr1). parse_address(Config) when is_list(Config) -> - V4Strict = + V4Reversable = [{{0,0,0,0},"0.0.0.0"}, - {{1,2,3,4},"1.2.3.4"}, + {{1,2,3,4},"1.2.3.4"}, {{253,252,251,250},"253.252.251.250"}, {{1,2,255,254},"1.2.255.254"}], - V6Strict = + V6Reversable = [{{0,0,0,0,0,0,0,0},"::"}, + {{0,0,0,0,0,0,0,1},"::1"}, + {{0,0,0,0,0,0,0,2},"::0.0.0.2"}, {{15,0,0,0,0,0,0,2},"f::2"}, - {{15,16#f11,0,0,0,0,256,2},"f:f11::0100:2"}, - {{0,0,0,0,0,0,0,16#17},"::17"}, - {{16#700,0,0,0,0,0,0,0},"0700::"}, - {{0,0,0,0,0,0,2,1},"::2:1"}, + {{15,16#f11,0,0,0,0,256,2},"f:f11::100:2"}, + {{16#700,0,0,0,0,0,0,0},"700::"}, + {{0,0,0,0,0,0,2,1},"::0.2.0.1"}, {{0,0,0,0,0,3,2,1},"::3:2:1"}, {{0,0,0,0,4,3,2,1},"::4:3:2:1"}, {{0,0,0,5,4,3,2,1},"::5:4:3:2:1"}, {{0,0,6,5,4,3,2,1},"::6:5:4:3:2:1"}, - {{0,7,6,5,4,3,2,1},"::7:6:5:4:3:2:1"}, + {{0,7,6,5,4,3,2,1},"0:7:6:5:4:3:2:1"}, {{7,0,0,0,0,0,0,0},"7::"}, {{7,6,0,0,0,0,0,0},"7:6::"}, {{7,6,5,0,0,0,0,0},"7:6:5::"}, {{7,6,5,4,0,0,0,0},"7:6:5:4::"}, {{7,6,5,4,3,0,0,0},"7:6:5:4:3::"}, {{7,6,5,4,3,2,0,0},"7:6:5:4:3:2::"}, - {{7,6,5,4,3,2,1,0},"7:6:5:4:3:2:1::"}, + {{7,6,5,4,3,2,1,0},"7:6:5:4:3:2:1:0"}, + {{0,0,6,5,4,3,0,0},"::6:5:4:3:0:0"}, + {{0,0,6,5,4,0,0,0},"0:0:6:5:4::"}, + {{8,0,0,5,4,0,0,1},"8::5:4:0:0:1"}, + {{8,0,0,5,0,0,0,1},"8:0:0:5::1"}, + {{0,7,6,5,4,3,2,0},"0:7:6:5:4:3:2:0"}, + {{0,0,6,5,4,3,0,0},"::6:5:4:3:0:0"}, + {{0,0,0,5,4,0,0,0},"::5:4:0:0:0"}, + {{0,0,0,0,4,0,0,0},"::4:0:0:0"}, + {{0,0,0,5,0,0,0,0},"0:0:0:5::"}, {{16#c11,16#c22,16#5c33,16#c440,16#55c0,16#c66c,16#77,16#88}, - "c11:0c22:5c33:c440:55c0:c66c:77:0088"}, + "c11:c22:5c33:c440:55c0:c66c:77:88"}, + {{0,16#c22,16#5c33,16#c440,16#55c0,16#c66c,16#77,16#88}, + "0:c22:5c33:c440:55c0:c66c:77:88"}, {{16#c11,0,16#5c33,16#c440,16#55c0,16#c66c,16#77,16#88}, - "c11::5c33:c440:55c0:c66c:77:0088"}, + "c11:0:5c33:c440:55c0:c66c:77:88"}, {{16#c11,16#c22,0,16#c440,16#55c0,16#c66c,16#77,16#88}, - "c11:0c22::c440:55c0:c66c:77:0088"}, + "c11:c22:0:c440:55c0:c66c:77:88"}, {{16#c11,16#c22,16#5c33,0,16#55c0,16#c66c,16#77,16#88}, - "c11:0c22:5c33::55c0:c66c:77:0088"}, + "c11:c22:5c33:0:55c0:c66c:77:88"}, {{16#c11,16#c22,16#5c33,16#c440,0,16#c66c,16#77,16#88}, - "c11:0c22:5c33:c440::c66c:77:0088"}, + "c11:c22:5c33:c440:0:c66c:77:88"}, {{16#c11,16#c22,16#5c33,16#c440,16#55c0,0,16#77,16#88}, - "c11:0c22:5c33:c440:55c0::77:0088"}, + "c11:c22:5c33:c440:55c0:0:77:88"}, {{16#c11,16#c22,16#5c33,16#c440,16#55c0,16#c66c,0,16#88}, - "c11:0c22:5c33:c440:55c0:c66c::0088"}, + "c11:c22:5c33:c440:55c0:c66c:0:88"}, + {{16#c11,16#c22,16#5c33,16#c440,16#55c0,16#c66c,16#77,0}, + "c11:c22:5c33:c440:55c0:c66c:77:0"}, + {{0,0,16#5c33,16#c440,16#55c0,16#c66c,16#77,16#88}, + "::5c33:c440:55c0:c66c:77:88"}, {{16#c11,0,0,16#c440,16#55c0,16#c66c,16#77,16#88}, - "c11::c440:55c0:c66c:77:0088"}, + "c11::c440:55c0:c66c:77:88"}, {{16#c11,16#c22,0,0,16#55c0,16#c66c,16#77,16#88}, - "c11:0c22::55c0:c66c:77:0088"}, + "c11:c22::55c0:c66c:77:88"}, {{16#c11,16#c22,16#5c33,0,0,16#c66c,16#77,16#88}, - "c11:0c22:5c33::c66c:77:0088"}, + "c11:c22:5c33::c66c:77:88"}, {{16#c11,16#c22,16#5c33,16#c440,0,0,16#77,16#88}, - "c11:0c22:5c33:c440::77:0088"}, + "c11:c22:5c33:c440::77:88"}, {{16#c11,16#c22,16#5c33,16#c440,16#55c0,0,0,16#88}, - "c11:0c22:5c33:c440:55c0::0088"}, + "c11:c22:5c33:c440:55c0::88"}, + {{16#c11,16#c22,16#5c33,16#c440,16#55c0,16#c66c,0,0}, + "c11:c22:5c33:c440:55c0:c66c::"}, + {{0,0,0,16#c440,16#55c0,16#c66c,16#77,16#88}, + "::c440:55c0:c66c:77:88"}, {{16#c11,0,0,0,16#55c0,16#c66c,16#77,16#88}, - "c11::55c0:c66c:77:0088"}, + "c11::55c0:c66c:77:88"}, {{16#c11,16#c22,0,0,0,16#c66c,16#77,16#88}, - "c11:0c22::c66c:77:0088"}, + "c11:c22::c66c:77:88"}, {{16#c11,16#c22,16#5c33,0,0,0,16#77,16#88}, - "c11:0c22:5c33::77:0088"}, + "c11:c22:5c33::77:88"}, {{16#c11,16#c22,16#5c33,16#c440,0,0,0,16#88}, - "c11:0c22:5c33:c440::0088"}, + "c11:c22:5c33:c440::88"}, + {{16#c11,16#c22,16#5c33,16#c440,16#55c0,0,0,0}, + "c11:c22:5c33:c440:55c0::"}, + {{0,0,0,0,16#55c0,16#c66c,16#77,16#88}, + "::55c0:c66c:77:88"}, {{16#c11,0,0,0,0,16#c66c,16#77,16#88}, - "c11::c66c:77:0088"}, + "c11::c66c:77:88"}, {{16#c11,16#c22,0,0,0,0,16#77,16#88}, - "c11:0c22::77:0088"}, + "c11:c22::77:88"}, {{16#c11,16#c22,16#5c33,0,0,0,0,16#88}, - "c11:0c22:5c33::0088"}, + "c11:c22:5c33::88"}, + {{16#c11,16#c22,16#5c33,16#c440,0,0,0,0}, + "c11:c22:5c33:c440::"}, + {{0,0,0,0,0,16#c66c,16#77,16#88}, + "::c66c:77:88"}, {{16#c11,0,0,0,0,0,16#77,16#88}, - "c11::77:0088"}, + "c11::77:88"}, {{16#c11,16#c22,0,0,0,0,0,16#88}, - "c11:0c22::0088"}, - {{0,0,0,0,0,65535,258,65534},"::FFFF:1.2.255.254"}, + "c11:c22::88"}, + {{16#c11,16#c22,16#5c33,0,0,0,0,0}, + "c11:c22:5c33::"}, + {{0,0,0,0,0,65535,258,65534},"::ffff:1.2.255.254"}, + {{16#fe80,12345,0,0,0,0,0,16#12},"fe80::12%012345"}, {{16#ffff,16#ffff,16#ffff,16#ffff,16#ffff,16#ffff,16#ffff,16#ffff}, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"} - |[{{D2,0,0,0,0,P,(D1 bsl 8) bor D2,(D3 bsl 8) bor D4}, - erlang:integer_to_list(D2, 16)++"::"++Q++S} - || {{D1,D2,D3,D4},S} <- V4Strict, - {P,Q} <- [{0,""},{16#17,"17:"},{16#ff0,"0ff0:"}]]], + |[{list_to_tuple(P++[(D1 bsl 8) bor D2,(D3 bsl 8) bor D4]), + Q++S} + || {{D1,D2,D3,D4},S} <- + tl(V4Reversable), + {P,Q} <- + [{[0,0,0,0,0,16#ffff],"::ffff:"}, + {[0,0,0,0,0,0],"::"}]]], V4Sloppy = [{{10,1,16#98,16#76},"10.0x019876"}, {{8#12,1,8#130,8#321},"012.01.054321"}, - {{255,255,255,255},"255.255.255.0377"}, - {{255,255,255,255},"0Xff.000000000377.0x0000ff.255"}, - {{255,255,255,255},"255.255.65535"}, - {{255,255,255,255},"255.0xFF.0177777"}, - {{255,255,255,255},"255.16777215"}, - {{255,255,255,255},"00377.0XFFFFFF"}, - {{255,255,255,255},"4294967295"}, - {{255,255,255,255},"0xffffffff"}, - {{255,255,255,255},"00000000000037777777777"}, + {{252,253,254,255},"252.253.254.0377"}, + {{252,253,254,255},"0Xfc.000000000375.0x0000fe.255"}, + {{252,253,254,255},"252.253.65279"}, + {{252,253,254,255},"252.0xFD.0177377"}, + {{252,253,254,255},"252.16645887"}, + {{252,253,254,255},"00374.0XFDFEFF"}, + {{252,253,254,255},"4244504319"}, + {{252,253,254,255},"0xfcfdfeff"}, + {{252,253,254,255},"00000000000037477377377"}, {{16#12,16#34,16#56,16#78},"0x12345678"}, {{16#12,16#34,16#56,16#78},"0x12.0x345678"}, {{16#12,16#34,16#56,16#78},"0x12.0X34.0x5678"}, @@ -546,8 +578,14 @@ parse_address(Config) when is_list(Config) -> {{0,0,0,0},"0.00.0.0"}, {{0,0,0,0},"0.0.000000000000.0"}], V6Sloppy = - [{{0,0,0,0,0,65535,(D1 bsl 8) bor D2,(D3 bsl 8) bor D4},S} - || {{D1,D2,D3,D4},S} <- V4Strict++V4Sloppy], + [{{16#a,16#b,16#c,16#0,16#0,16#d,16#e,16#f},"A:B:C::d:e:f"}, + {{16#fe80,0,0,0,0,0,0,16#12},"fe80::12%XXXXXXX"}] + ++ + [{{P,0,0,0,0,D2,(D1 bsl 8) bor D2,(D3 bsl 8) bor D4}, + Q++erlang:integer_to_list(D2, 16)++":"++S} + || {{D1,D2,D3,D4},S} <- V4Reversable, + {P,Q} <- + [{16#2001,"2001::"},{16#177,"177::"},{16#ff0,"Ff0::"}]], V4Err = ["0.256.0.1", "1.2.3.4.5", @@ -569,8 +607,11 @@ parse_address(Config) when is_list(Config) -> "::-1", "::g", "f:f11::10100:2", + "f:f11::01100:2", "::17000", + "::01700", "10000::", + "01000::", "::8:7:6:5:4:3:2:1", "8:7:6:5:4:3:2:1::", "8:7:6:5:4::3:2:1", @@ -588,28 +629,36 @@ parse_address(Config) when is_list(Config) -> "fec0::fFfF:127.0.0.1."], t_parse_address (parse_ipv6_address, - V6Strict++V6Sloppy++V6Err++V4Err), + false, + V6Reversable++V6Sloppy++V6Err++V4Err), t_parse_address (parse_ipv6strict_address, - V6Strict++V6Err++V4Err++[S || {_,S} <- V6Sloppy]), + true, + V6Reversable++V6Err++V4Err), t_parse_address (parse_ipv4_address, - V4Strict++V4Sloppy++V4Err++V6Err++[S || {_,S} <- V6Strict]), + false, + V4Reversable++V4Sloppy++V4Err++V6Err++[S || {_,S} <- V6Reversable]), t_parse_address (parse_ipv4strict_address, - V4Strict++V4Err++V6Err++[S || {_,S} <- V4Sloppy++V6Strict]). + true, + V4Reversable++V4Err++V6Err++[S || {_,S} <- V4Sloppy++V6Reversable]). -t_parse_address(Func, []) -> +t_parse_address(Func, _Reversable, []) -> io:format("~p done.~n", [Func]), ok; -t_parse_address(Func, [{Addr,String}|L]) -> +t_parse_address(Func, Reversable, [{Addr,String}|L]) -> io:format("~p = ~p.~n", [Addr,String]), {ok,Addr} = inet:Func(String), - t_parse_address(Func, L); -t_parse_address(Func, [String|L]) -> + case Reversable of + true ->String = inet:ntoa(Addr); + false -> ok + end, + t_parse_address(Func, Reversable, L); +t_parse_address(Func, Reversable, [String|L]) -> io:format("~p.~n", [String]), {error,einval} = inet:Func(String), - t_parse_address(Func, L). + t_parse_address(Func, Reversable, L). parse_strict_address(Config) when is_list(Config) -> {ok, {127,0,0,1}} = @@ -619,42 +668,57 @@ parse_strict_address(Config) when is_list(Config) -> {ok, {3089,3106,23603,50240,0,0,119,136}} = inet:parse_strict_address("c11:0c22:5c33:c440::077:0088"). -t_gethostnative(suite) ->[]; -t_gethostnative(doc) ->[]; +ipv4_mapped_ipv6_address(Config) when is_list(Config) -> + {D1,D2,D3,D4} = IPv4Address = + {rand:uniform(256) - 1, + rand:uniform(256) - 1, + rand:uniform(256) - 1, + rand:uniform(256) - 1}, + E7 = (D1 bsl 8) bor D2, + E8 = (D3 bsl 8) bor D4, + io:format("IPv4Address: ~p.~n", [IPv4Address]), + {0,0,0,0,0,65535,E7,E8} = inet:ipv4_mapped_ipv6_address(IPv4Address), + IPv6Address = + {rand:uniform(65536) - 1, + rand:uniform(65536) - 1, + rand:uniform(65536) - 1, + rand:uniform(65536) - 1, + rand:uniform(65536) - 1, + rand:uniform(65536) - 1, E7, E8}, + IPv4Address = inet:ipv4_mapped_ipv6_address(IPv6Address), + ok. + t_gethostnative(Config) when is_list(Config) -> -%% this will result in 26 bytes sent which causes problem in Windows -%% if the port-program has not assured stdin to be read in BINARY mode -%% OTP-2555 - ?line case inet_gethost_native:gethostbyname( - "a23456789012345678901234") of + %% this will result in 26 bytes sent which causes problem in Windows + %% if the port-program has not assured stdin to be read in BINARY mode + %% OTP-2555 + case inet_gethost_native:gethostbyname( + "a23456789012345678901234") of {error,notfound} -> - ?line ok; + ok; {error,no_data} -> - ?line ok + ok end. -gethostnative_parallell(suite) -> - []; -gethostnative_parallell(doc) -> - ["Check that the emulator survives crashes in gethost_native"]; +%% Check that the emulator survives crashes in gethost_native. gethostnative_parallell(Config) when is_list(Config) -> - ?line {ok,Hostname} = inet:gethostname(), - ?line {ok,_} = inet:gethostbyname(Hostname), + {ok,Hostname} = inet:gethostname(), + {ok,_} = inet:gethostbyname(Hostname), case whereis(inet_gethost_native) of Pid when is_pid(Pid) -> - ?line do_gethostnative_parallell(); + do_gethostnative_parallell(); _ -> - ?line {skipped, "Not running native gethostbyname"} + {skipped, "Not running native gethostbyname"} end. do_gethostnative_parallell() -> - ?line PA = filename:dirname(code:which(?MODULE)), - ?line {ok,Node} = ?t:start_node(gethost_parallell, slave, - [{args, "-pa " ++ PA}]), - ?line ok = rpc:call(Node, ?MODULE, parallell_gethost, []), - ?line receive after 10000 -> ok end, - ?line pong = net_adm:ping(Node), - ?line ?t:stop_node(Node), + PA = filename:dirname(code:which(?MODULE)), + {ok,Node} = test_server:start_node(gethost_parallell, slave, + [{args, "-pa " ++ PA}]), + ok = rpc:call(Node, ?MODULE, parallell_gethost, []), + receive after 10000 -> ok end, + pong = net_adm:ping(Node), + test_server:stop_node(Node), ok. parallell_gethost() -> @@ -747,7 +811,7 @@ wait_for_gethost(N) -> %% This is what I call an exit tuple :) exit({inet,gethostbyname, returned, Otherwise, 'when', 'N','=',N,'and','hostname','=',Hostname,'and', - kill_gethost_n,'=',get(kill_gethost_n)}) + kill_gethost_n,'=',get(kill_gethost_n)}) end, case whereis(inet_gethost_native) of Pid when is_pid(Pid) -> @@ -759,23 +823,20 @@ wait_for_gethost(N) -> end, wait_for_gethost(N-1) end. - -cname_loop(suite) -> - []; -cname_loop(doc) -> - ["Check that the resolver handles a CNAME loop"]; + +%% Check that the resolver handles a CNAME loop. cname_loop(Config) when is_list(Config) -> %% getbyname (hostent_by_domain) - ?line ok = inet_db:add_rr("mydomain.com", in, ?S_CNAME, ttl, "mydomain.com"), - ?line {error,nxdomain} = inet_db:getbyname("mydomain.com", ?S_A), - ?line ok = inet_db:del_rr("mydomain.com", in, ?S_CNAME, "mydomain.com"), + ok = inet_db:add_rr("mydomain.com", in, ?S_CNAME, ttl, "mydomain.com"), + {error,nxdomain} = inet_db:getbyname("mydomain.com", ?S_A), + ok = inet_db:del_rr("mydomain.com", in, ?S_CNAME, "mydomain.com"), %% res_hostent_by_domain RR = #dns_rr{domain = "mydomain.com", class = in, type = ?S_CNAME, data = "mydomain.com"}, Rec = #dns_rec{anlist = [RR]}, - ?line {error,nxdomain} = inet_db:res_hostent_by_domain("mydomain.com", ?S_A, Rec), + {error,nxdomain} = inet_db:res_hostent_by_domain("mydomain.com", ?S_A, Rec), ok. @@ -790,80 +851,75 @@ cname_loop(Config) when is_list(Config) -> lookup_processes=20}). gethostnative_soft_restart() -> required(hosts). -gethostnative_soft_restart(suite) -> - []; -gethostnative_soft_restart(doc) -> - ["Check that no name lookups fails during soft restart " - "of inet_gethost_native"]; + +%% Check that no name lookups fails during soft restart +%% of inet_gethost_native. gethostnative_soft_restart(Config) when is_list(Config) -> - ?line gethostnative_control(Config, - #gethostnative_control{ - control_seq=[soft_restart]}). + gethostnative_control(Config, + #gethostnative_control{ + control_seq=[soft_restart]}). gethostnative_debug_level() -> required(hosts). -gethostnative_debug_level(suite) -> - []; -gethostnative_debug_level(doc) -> - ["Check that no name lookups fails during debug level change " - "of inet_gethost_native"]; + +%% Check that no name lookups fails during debug level change +%% of inet_gethost_native. gethostnative_debug_level(Config) when is_list(Config) -> - ?line gethostnative_control(Config, - #gethostnative_control{ - control_seq=[{debug_level,1}, - {debug_level,0}]}). + gethostnative_control(Config, + #gethostnative_control{ + control_seq=[{debug_level,1}, + {debug_level,0}]}). gethostnative_control(Config, Optrec) -> - ?line case inet_db:res_option(lookup) of - [native] -> - case whereis(inet_gethost_native) of - Pid when is_pid(Pid) -> - ?line gethostnative_control_1(Config, Optrec); - _ -> - ?line {skipped, "Not running native gethostbyname"} - end; - _ -> - ?line {skipped, "Native not only lookup metod"} - end. + case inet_db:res_option(lookup) of + [native] -> + case whereis(inet_gethost_native) of + Pid when is_pid(Pid) -> + gethostnative_control_1(Config, Optrec); + _ -> + {skipped, "Not running native gethostbyname"} + end; + _ -> + {skipped, "Native not only lookup metod"} + end. gethostnative_control_1(Config, #gethostnative_control{ - control_seq=Seq, - control_interval=Interval, - lookup_delay=Delay, - lookup_count=Cnt, - lookup_processes=N}) -> - ?line {ok, Hostname} = inet:gethostname(), - ?line {ok, _} = inet:gethostbyname(Hostname), - ?line Hosts = + control_seq=Seq, + control_interval=Interval, + lookup_delay=Delay, + lookup_count=Cnt, + lookup_processes=N}) -> + {ok, Hostname} = inet:gethostname(), + {ok, _} = inet:gethostbyname(Hostname), + Hosts = [Hostname|[H || {_,H} <- get_hosts(Config)] ++[H++D || H <- ["www.","www1.","www2.",""], D <- ["erlang.org","erlang.se"]] ++[H++"cslab.ericsson.net" || H <- ["morgoth.","hades.","styx."]]], %% Spawn some processes to do parallel lookups while %% I repeatedly do inet_gethost_native:control/1. - ?line TrapExit = process_flag(trap_exit, true), - ?line gethostnative_control_2([undefined], Interval, Delay, Cnt, N, Hosts), - ?line test_server:format( - "First intermission: now starting control sequence ~w\n", - [Seq]), - ?line erlang:display(first_intermission), - ?line gethostnative_control_2(Seq, Interval, Delay, Cnt, N, Hosts), - ?line erlang:display(second_intermission), - ?line test_server:format( - "Second intermission: now stopping control sequence ~w\n", - [Seq]), - ?line gethostnative_control_2([undefined], Interval, Delay, Cnt, N, Hosts), - ?line true = process_flag(trap_exit, TrapExit), - ?line ok. + TrapExit = process_flag(trap_exit, true), + gethostnative_control_2([undefined], Interval, Delay, Cnt, N, Hosts), + io:format( + "First intermission: now starting control sequence ~w\n", + [Seq]), + erlang:display(first_intermission), + gethostnative_control_2(Seq, Interval, Delay, Cnt, N, Hosts), + erlang:display(second_intermission), + io:format( + "Second intermission: now stopping control sequence ~w\n", + [Seq]), + gethostnative_control_2([undefined], Interval, Delay, Cnt, N, Hosts), + true = process_flag(trap_exit, TrapExit), + ok. gethostnative_control_2(Seq, Interval, Delay, Cnt, N, Hosts) -> - ?line Tag = make_ref(), - ?line Parent = self(), - ?line Lookupers = + Tag = make_ref(), + Parent = self(), + Lookupers = [spawn_link( fun () -> - random:seed(), lookup_loop(Hosts, Delay, Tag, Parent, Cnt, Hosts) end) || _ <- lists:seq(1, N)], @@ -873,7 +929,7 @@ gethostnative_control_2(Seq, Interval, Delay, Cnt, N, Hosts) -> gethostnative_control_3(Tag, Reason) -> receive {Tag,Error} -> - ?line gethostnative_control_3(Tag, Error) + gethostnative_control_3(Tag, Error) after 0 -> Reason end. @@ -888,27 +944,26 @@ control_loop([Op|Ops], Interval, Tag, Lookupers, Seq) -> Seq). control_loop_1(Op, Interval, Tag, Lookupers) -> - ?line - receive - {'EXIT',Pid,Reason} -> - ?line case Reason of - Tag -> % Done - ?line control_loop_1 - (Op, Interval, Tag, - lists:delete(Pid, Lookupers)); - _ -> - ?line io:format("Lookuper ~p died: ~p", - [Pid,Reason]), - ?line test_server:fail("Lookuper died") - end - after Interval -> - ?line if Op =/= undefined -> - ?line ok = inet_gethost_native:control(Op); - true -> - ?line ok - end, - ?line Lookupers - end. + receive + {'EXIT',Pid,Reason} -> + case Reason of + Tag -> % Done + control_loop_1 + (Op, Interval, Tag, + lists:delete(Pid, Lookupers)); + _ -> + io:format("Lookuper ~p died: ~p", + [Pid,Reason]), + ct:fail("Lookuper died") + end + after Interval -> + if Op =/= undefined -> + ok = inet_gethost_native:control(Op); + true -> + ok + end, + Lookupers + end. lookup_loop(_, _Delay, Tag, _Parent, 0, _Hosts) -> exit(Tag); @@ -919,21 +974,18 @@ lookup_loop([H|Hs], Delay, Tag, Parent, Cnt, Hosts) -> {ok,_Hent} -> ok; {error,nxdomain} -> ok; Error -> - ?line io:format("Name lookup error for ~p for ~p: ~p", - [self(),H,Error]), + io:format("Name lookup error for ~p for ~p: ~p", + [self(),H,Error]), Parent ! {Tag,Error} end, receive - after random:uniform(Delay) -> + after rand:uniform(Delay) -> lookup_loop(Hs, Delay, Tag, Parent, Cnt-1, Hosts) end. -lookup_bad_search_option(suite) -> - []; -lookup_bad_search_option(doc) -> - ["Test lookup with erroneously configured lookup option (OTP-12133)"]; +%% Test lookup with erroneously configured lookup option (OTP-12133). lookup_bad_search_option(Config) when is_list(Config) -> %% Manipulation of resolver config is done in init_per_testcase %% and end_per_testcase to ensure cleanup. @@ -943,24 +995,21 @@ lookup_bad_search_option(Config) when is_list(Config) -> -getif(suite) -> - []; -getif(doc) -> - ["Tests basic functionality of getiflist, getif, and ifget"]; +%% Tests basic functionality of getiflist, getif, and ifget. getif(Config) when is_list(Config) -> - ?line case os:type() of - {unix,Osname} -> - ?line do_getif(Osname); - {_,_} -> - {skip,"inet:getif/0 probably not supported"} - end. + case os:type() of + {unix,Osname} -> + do_getif(Osname); + {_,_} -> + {skip,"inet:getif/0 probably not supported"} + end. do_getif(Osname) -> - ?line {ok,Hostname} = inet:gethostname(), - ?line {ok,Address} = inet:getaddr(Hostname, inet), - ?line {ok,Loopback} = inet:getaddr("localhost", inet), - ?line {ok,Interfaces} = inet:getiflist(), - ?line HWAs = + {ok,Hostname} = inet:gethostname(), + {ok,Address} = inet:getaddr(Hostname, inet), + {ok,Loopback} = inet:getaddr("localhost", inet), + {ok,Interfaces} = inet:getiflist(), + HWAs = lists:sort( lists:foldl( fun (I, Acc) -> @@ -969,10 +1018,10 @@ do_getif(Osname) -> {ok,[]} -> Acc end end, [], Interfaces)), - ?line io:format("HWAs = ~p~n", [HWAs]), - ?line (Osname =/= sunos) - andalso ((length(HWAs) > 0) orelse (?t:fail(no_HWAs))), - ?line Addresses = + io:format("HWAs = ~p~n", [HWAs]), + (Osname =/= sunos) + andalso ((length(HWAs) > 0) orelse (ct:fail(no_HWAs))), + Addresses = lists:sort( lists:foldl( fun (I, Acc) -> @@ -981,139 +1030,145 @@ do_getif(Osname) -> {ok,[]} -> Acc end end, [], Interfaces)), - ?line {ok,Getif} = inet:getif(), - ?line Addresses = lists:sort([A || {A,_,_} <- Getif]), - ?line true = ip_member(Address, Addresses), - ?line true = ip_member(Loopback, Addresses), - ?line ok. - -getif_ifr_name_overflow(doc) -> - "Test long interface names do not overrun buffer"; + {ok,Getif} = inet:getif(), + Addresses = lists:sort([A || {A,_,_} <- Getif]), + true = ip_member(Address, Addresses), + true = ip_member(Loopback, Addresses), + ok. + +%% Test long interface names do not overrun buffer. getif_ifr_name_overflow(Config) when is_list(Config) -> - ?line case os:type() of - {unix,Osname} -> - ?line do_getif_ifr_name_overflow(Osname); - {_,_} -> - {skip,"inet:ifget/2 probably not supported"} - end. + case os:type() of + {unix,Osname} -> + do_getif_ifr_name_overflow(Osname); + {_,_} -> + {skip,"inet:ifget/2 probably not supported"} + end. do_getif_ifr_name_overflow(_) -> %% emulator should not crash - ?line {ok,[]} = inet:ifget(lists:duplicate(128, "x"), [addr]), + {ok,[]} = inet:ifget(lists:duplicate(128, "x"), [addr]), ok. -getservbyname_overflow(doc) -> - "Test long service names do not overrun buffer"; +%% Test long service names do not overrun buffer. getservbyname_overflow(Config) when is_list(Config) -> %% emulator should not crash - ?line {error,einval} = inet:getservbyname(list_to_atom(lists:flatten(lists:duplicate(128, "x"))), tcp), + {error,einval} = inet:getservbyname(list_to_atom(lists:flatten(lists:duplicate(128, "x"))), tcp), ok. -getifaddrs(doc) -> - "Test inet:gifaddrs/0"; +%% Test inet:gifaddrs/0. getifaddrs(Config) when is_list (Config) -> - ?line {ok,IfAddrs} = inet:getifaddrs(), - ?line ?t:format("IfAddrs = ~p.~n", [IfAddrs]), - ?line - case - {os:type(), - [If || - {If,Opts} <- IfAddrs, - lists:keymember(hwaddr, 1, Opts)]} of - {{unix,sunos},[]} -> ok; - {OT,[]} -> - ?t:fail({should_have_hwaddr,OT}); - _ -> ok - end, - ?line Addrs = + {ok,IfAddrs} = inet:getifaddrs(), + io:format("IfAddrs = ~p.~n", [IfAddrs]), + case + {os:type(), + [If || + {If,Opts} <- IfAddrs, + lists:keymember(hwaddr, 1, Opts)]} of + {{unix,sunos},[]} -> ok; + {OT,[]} -> + ct:fail({should_have_hwaddr,OT}); + _ -> ok + end, + Addrs = [element(1, A) || A <- ifaddrs(IfAddrs)], - ?line ?t:format("Addrs = ~p.~n", [Addrs]), - ?line [check_addr(Addr) || Addr <- Addrs], + io:format("Addrs = ~p.~n", [Addrs]), + [check_addr(Addr) || Addr <- Addrs], ok. -check_addr(Addr) +check_addr({addr,Addr}) when tuple_size(Addr) =:= 8, element(1, Addr) band 16#FFC0 =:= 16#FE80 -> - ?line ?t:format("Addr: ~p link local; SKIPPED!~n", [Addr]), + io:format("Addr: ~p link local; SKIPPED!~n", [Addr]), ok; -check_addr(Addr) -> - ?line ?t:format("Addr: ~p.~n", [Addr]), - ?line Ping = "ping", - ?line Pong = "pong", - ?line {ok,L} = gen_tcp:listen(0, [{ip,Addr},{active,false}]), - ?line {ok,P} = inet:port(L), - ?line {ok,S1} = gen_tcp:connect(Addr, P, [{active,false}]), - ?line {ok,S2} = gen_tcp:accept(L), - ?line ok = gen_tcp:send(S2, Ping), - ?line {ok,Ping} = gen_tcp:recv(S1, length(Ping)), - ?line ok = gen_tcp:send(S1, Pong), - ?line ok = gen_tcp:close(S1), - ?line {ok,Pong} = gen_tcp:recv(S2, length(Pong)), - ?line ok = gen_tcp:close(S2), - ?line ok = gen_tcp:close(L), - ok. +check_addr({addr,Addr}) -> + io:format("Addr: ~p.~n", [Addr]), + Ping = "ping", + Pong = "pong", + {ok,L} = gen_tcp:listen(0, [{ip,Addr},{active,false}]), + {ok,P} = inet:port(L), + {ok,S1} = gen_tcp:connect(Addr, P, [{active,false}]), + {ok,S2} = gen_tcp:accept(L), + ok = gen_tcp:send(S2, Ping), + {ok,Ping} = gen_tcp:recv(S1, length(Ping)), + ok = gen_tcp:send(S1, Pong), + ok = gen_tcp:close(S1), + {ok,Pong} = gen_tcp:recv(S2, length(Pong)), + ok = gen_tcp:close(S2), + ok = gen_tcp:close(L). -record(ifopts, {name,flags,addrs=[],hwaddr}). ifaddrs([]) -> []; ifaddrs([{If,Opts}|IOs]) -> - ?line #ifopts{flags=Flags} = Ifopts = - check_ifopts(Opts, #ifopts{name=If}), - ?line case Flags =/= undefined andalso lists:member(up, Flags) of - true -> - Ifopts#ifopts.addrs; - false -> - [] - end++ifaddrs(IOs). - -check_ifopts([], #ifopts{name=If,flags=Flags,addrs=Raddrs}=Ifopts) -> + #ifopts{flags=F} = Ifopts = check_ifopts(Opts, #ifopts{name=If}), + case F of + {flags,Flags} -> + case lists:member(running, Flags) of + true -> Ifopts#ifopts.addrs; + false -> [] + end ++ ifaddrs(IOs); + undefined -> + ifaddrs(IOs) + end. + +check_ifopts([], #ifopts{flags=F,addrs=Raddrs}=Ifopts) -> Addrs = lists:reverse(Raddrs), R = Ifopts#ifopts{addrs=Addrs}, - ?t:format("~p.~n", [R]), + io:format("~p.~n", [R]), %% See how we did... - if is_list(Flags) -> ok; - true -> - ?t:fail({flags_undefined,If}) - end, + {flags,Flags} = F, case lists:member(broadcast, Flags) of true -> [case A of - {_,_,_} -> A; - {T,_} when tuple_size(T) =:= 8 -> A; - _ -> - ?t:fail({broaddr_missing,If,A}) + {{addr,_},{netmask,_},{broadaddr,_}} -> + A; + {{addr,T},{netmask,_}} when tuple_size(T) =:= 8 -> + A end || A <- Addrs]; false -> - [case A of {_,_} -> A; - _ -> - ?t:fail({should_have_netmask,If,A}) - end || A <- Addrs] + case lists:member(pointtopoint, Flags) of + true -> + [case A of + {{addr,_},{netmask,_},{dstaddr,_}} -> + A + end || A <- Addrs]; + false -> + [case A of + {{addr,_},{netmask,_}} -> + A + end || A <- Addrs] + end end, R; -check_ifopts([{flags,Flags}|Opts], #ifopts{flags=undefined}=Ifopts) -> - check_ifopts(Opts, Ifopts#ifopts{flags=Flags}); -check_ifopts([{flags,Fs}|Opts], #ifopts{flags=Flags}=Ifopts) -> - case Fs of +check_ifopts([{flags,_}=F|Opts], #ifopts{flags=undefined}=Ifopts) -> + check_ifopts(Opts, Ifopts#ifopts{flags=F}); +check_ifopts([{flags,_}=F|Opts], #ifopts{flags=Flags}=Ifopts) -> + case F of Flags -> - check_ifopts(Opts, Ifopts#ifopts{}); + check_ifopts(Opts, Ifopts); _ -> - ?t:fail({multiple_flags,Fs,Ifopts}) + ct:fail({multiple_flags,F,Ifopts}) end; check_ifopts( - [{addr,Addr},{netmask,Netmask},{broadaddr,Broadaddr}|Opts], + [{addr,_}=A,{netmask,_}=N,{dstaddr,_}=D|Opts], + #ifopts{addrs=Addrs}=Ifopts) -> + check_ifopts(Opts, Ifopts#ifopts{addrs=[{A,N,D}|Addrs]}); +check_ifopts( + [{addr,_}=A,{netmask,_}=N,{broadaddr,_}=B|Opts], #ifopts{addrs=Addrs}=Ifopts) -> - check_ifopts(Opts, Ifopts#ifopts{addrs=[{Addr,Netmask,Broadaddr}|Addrs]}); + check_ifopts(Opts, Ifopts#ifopts{addrs=[{A,N,B}|Addrs]}); check_ifopts( - [{addr,Addr},{netmask,Netmask}|Opts], + [{addr,_}=A,{netmask,_}=N|Opts], #ifopts{addrs=Addrs}=Ifopts) -> - check_ifopts(Opts, Ifopts#ifopts{addrs=[{Addr,Netmask}|Addrs]}); -check_ifopts([{addr,Addr}|Opts], #ifopts{addrs=Addrs}=Ifopts) -> - check_ifopts(Opts, Ifopts#ifopts{addrs=[{Addr}|Addrs]}); -check_ifopts([{hwaddr,Hwaddr}|Opts], #ifopts{hwaddr=undefined}=Ifopts) + check_ifopts(Opts, Ifopts#ifopts{addrs=[{A,N}|Addrs]}); +check_ifopts([{addr,_}=A|Opts], #ifopts{addrs=Addrs}=Ifopts) -> + check_ifopts(Opts, Ifopts#ifopts{addrs=[{A}|Addrs]}); +check_ifopts([{hwaddr,Hwaddr}=H|Opts], #ifopts{hwaddr=undefined}=Ifopts) when is_list(Hwaddr) -> - check_ifopts(Opts, Ifopts#ifopts{hwaddr=Hwaddr}); -check_ifopts([{hwaddr,HwAddr}|_], #ifopts{}=Ifopts) -> - ?t:fail({multiple_hwaddrs,HwAddr,Ifopts}). + check_ifopts(Opts, Ifopts#ifopts{hwaddr=H}); +check_ifopts([{hwaddr,_}=H|_], #ifopts{}=Ifopts) -> + ct:fail({multiple_hwaddrs,H,Ifopts}). %% Works just like lists:member/2, except that any {127,_,_,_} tuple %% matches any other {127,_,_,_}. We do this to handle Linux systems @@ -1147,9 +1202,13 @@ simple_netns(Config) when is_list(Config) -> jog_netns_opt(L), ok = gen_tcp:close(L), %% - {ok,S} = gen_sctp:open(), - jog_netns_opt(S), - ok = gen_sctp:close(S); + case gen_sctp:open() of + {ok,S} -> + jog_netns_opt(S), + ok = gen_sctp:close(S); + {error,eprotonosupport} -> + ok + end; {error,einval} -> {skip,"setns() not supported"} end. @@ -1163,24 +1222,28 @@ jog_netns_opt(S) -> ok. +%% Smoke test netns support. simple_netns_open(Config) when is_list(Config) -> + %% Note: {error,enoent} will be returned if the run-time executable + %% has support for netns, but /proc/self/ns/net is missing. case gen_udp:open(0, [binary,{netns,"/"},inet]) of {ok,U} -> ok = gen_udp:close(U); - {error,E1} when E1 =:= einval; E1 =:= eperm -> + {error,E1} when E1 =:= einval; E1 =:= eperm; E1 =:= enoent -> ok end, case gen_tcp:listen(0, [binary,{netns,"/"},inet]) of {ok,T} -> ok = gen_tcp:close(T); - {error,E2} when E2 =:= einval; E2 =:= eperm -> + {error,E2} when E2 =:= einval; E2 =:= eperm; E2 =:= enoent -> ok end, try gen_sctp:open(0, [binary,{netns,"/"},inet]) of {ok,S} -> ok = gen_sctp:close(S); {error,E3} - when E3 =:= einval; E3 =:= eperm; E3 =:= eprotonosupport -> + when E3 =:= einval; E3 =:= eperm; + E3 =:= enoent; E3 =:= eprotonosupport -> ok catch error:badarg -> @@ -1253,3 +1316,67 @@ cmd(CmdString) -> io:put_chars(["# ",CmdString,io_lib:nl()]), io:put_chars([os:cmd(CmdString++" ; echo ' =>' $?")]), ok. + +-define(CAP_NET_RAW, 13). %% from /usr/include/linux/capability.h + +can_bind_to_device({unix, linux}, {Major, _, _}) + when Major > 2 -> + Status = os:cmd("cat /proc/self/status | grep CapEff"), + [_, CapEffStr] = string:tokens(Status, [$\n, $\t]), + CapEff = list_to_integer(CapEffStr, 16), + if CapEff band (1 bsl ?CAP_NET_RAW) =/= 0 -> + ok; + true -> + {skip,"insufficient capabilities, CAP_NET_RAW not granted"} + end; +can_bind_to_device(_OS, _Version) -> + {skip,"socket option bind_to_device not supported on this OS or version"}. + +simple_bind_to_device(Config) when is_list(Config) -> + case can_bind_to_device(os:type(), os:version()) of + ok -> + {ok,U} = gen_udp:open(0), + jog_bind_to_device_opt(U), + ok = gen_udp:close(U), + %% + {ok,L} = gen_tcp:listen(0, []), + jog_bind_to_device_opt(L), + ok = gen_tcp:close(L), + %% + case gen_sctp:open() of + {ok,S} -> + jog_bind_to_device_opt(S), + ok = gen_sctp:close(S); + {error,eprotonosupport} -> + ok + end; + Other -> + Other + end. + +%% Smoke test bind_to_device support. +simple_bind_to_device_open(Config) when is_list(Config) -> + case can_bind_to_device(os:type(), os:version()) of + ok -> + {ok,U} = gen_udp:open(0, [binary,{bind_to_device,<<"lo">>},inet]), + ok = gen_udp:close(U), + {ok,T} = gen_tcp:listen(0, [binary,{bind_to_device,<<"lo">>},inet]), + ok = gen_tcp:close(T), + + case gen_sctp:open(0, [binary,{bind_to_device,<<"lo">>},inet]) of + {ok,S} -> + ok = gen_sctp:close(S); + {error,eprotonosupport} -> + ok + end; + Other -> + Other + end. + +jog_bind_to_device_opt(S) -> + %% This is just jogging the option mechanics + ok = inet:setopts(S, [{bind_to_device,<<>>}]), + {ok,[{bind_to_device,<<>>}]} = inet:getopts(S, [bind_to_device]), + ok = inet:setopts(S, [{bind_to_device,<<"lo">>}]), + {ok,[{bind_to_device,<<"lo">>}]} = inet:getopts(S, [bind_to_device]), + ok. diff --git a/lib/kernel/test/inet_res_SUITE.erl b/lib/kernel/test/inet_res_SUITE.erl index 1bc93e3138..df6e48abae 100644 --- a/lib/kernel/test/inet_res_SUITE.erl +++ b/lib/kernel/test/inet_res_SUITE.erl @@ -1,25 +1,25 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2009-2013. All Rights Reserved. +%% Copyright Ericsson AB 2009-2018. 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/. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at %% -%% 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. +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% -module(inet_res_SUITE). -include_lib("common_test/include/ct.hrl"). --include("test_server_line.hrl"). -include_lib("kernel/include/inet.hrl"). -include_lib("kernel/src/inet_dns.hrl"). @@ -42,7 +42,24 @@ -define(RUN_NAMED, "run-named"). -suite() -> [{ct_hooks,[ts_install_cth]}]. +%% This test suite use a script ?RUN_NAMED that tries to start +%% a temporary local nameserver BIND 8 or 9 that must be installed +%% on your machine. +%% +%% For example, on Ubuntu 14.04, as root: +%% apt-get install bind9 +%% Now, that is not enough since Apparmor will not allow +%% the nameserver daemon /usr/sbin/named to read from the test directory. +%% Assuming that you run tests in /ldisk/daily_build, and still on +%% Ubuntu 14.04, make /usr/apparmor.d/local/usr.sbin.named contain: +%% /ldisk/daily_build/** r, +%% And yes; the trailing comma must be there... + + + +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap,{minutes,1}}]. all() -> [basic, resolve, edns0, txt_record, files_monitor, @@ -77,8 +94,8 @@ zone_dir(TC) -> end. init_per_testcase(Func, Config) -> - PrivDir = ?config(priv_dir, Config), - DataDir = ?config(data_dir, Config), + PrivDir = proplists:get_value(priv_dir, Config), + DataDir = proplists:get_value(data_dir, Config), try ns_init(zone_dir(Func), PrivDir, DataDir) of NsSpec -> Lookup = inet_db:res_option(lookup), @@ -88,29 +105,27 @@ init_per_testcase(Func, Config) -> inet_db:ins_alt_ns(IP, Port); _ -> ok end, - Dog = test_server:timetrap(test_server:seconds(20)), - [{nameserver,NsSpec},{res_lookup,Lookup},{watchdog,Dog}|Config] + [{nameserver,NsSpec},{res_lookup,Lookup}|Config] catch SkipReason -> {skip,SkipReason} end. end_per_testcase(_Func, Config) -> - test_server:timetrap_cancel(?config(watchdog, Config)), - inet_db:set_lookup(?config(res_lookup, Config)), - NsSpec = ?config(nameserver, Config), + inet_db:set_lookup(proplists:get_value(res_lookup, Config)), + NsSpec = proplists:get_value(nameserver, Config), case NsSpec of {_,{IP,Port},_} -> inet_db:del_alt_ns(IP, Port); _ -> ok end, - ns_end(NsSpec, ?config(priv_dir, Config)). + ns_end(NsSpec, proplists:get_value(priv_dir, Config)). %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Nameserver control ns(Config) -> - {_ZoneDir,NS,_P} = ?config(nameserver, Config), + {_ZoneDir,NS,_P} = proplists:get_value(nameserver, Config), NS. ns_init(ZoneDir, PrivDir, DataDir) -> @@ -119,7 +134,7 @@ ns_init(ZoneDir, PrivDir, DataDir) -> {unix,_} -> PortNum = case {os:type(),os:version()} of {{unix,solaris},{M,V,_}} when M =< 5, V < 10 -> - 11895 + random:uniform(100); + 11895 + rand:uniform(100); _ -> {ok,S} = gen_udp:open(0, [{reuseaddr,true}]), {ok,PNum} = inet:port(S), @@ -202,10 +217,10 @@ proxy_start(TC, {NS,P}) -> spawn_link( fun () -> try proxy_start(TC, NS, P, Parent, Tag) - catch C:X -> + catch C:X:Stacktrace -> io:format( "~w: ~w:~p ~p~n", - [self(),C,X,erlang:get_stacktrace()]) + [self(),C,X,Stacktrace]) end end), receive {started,Tag,Port} -> @@ -277,8 +292,7 @@ proxy_ns({proxy,_,_,ProxyNS}) -> ProxyNS. %% %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -basic(doc) -> - ["Lookup an A record with different API functions"]; +%% Lookup an A record with different API functions. basic(Config) when is_list(Config) -> NS = ns(Config), Name = "ns.otptest", @@ -340,8 +354,7 @@ basic(Config) when is_list(Config) -> %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -resolve(doc) -> - ["Lookup different records using resolve/2..4"]; +%% Lookup different records using resolve/2..4. resolve(Config) when is_list(Config) -> Class = in, NS = ns(Config), @@ -471,8 +484,7 @@ check_msg(Class, Type, Msg, AnList, NsList) -> %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -edns0(doc) -> - ["Test EDNS and truncation"]; +%% Test EDNS and truncation. edns0(Config) when is_list(Config) -> NS = ns(Config), Domain = "otptest", @@ -533,10 +545,7 @@ inet_res_filter(Anlist, Class, Type) -> %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -txt_record(suite) -> - []; -txt_record(doc) -> - ["Tests TXT records"]; +%% Tests TXT records. txt_record(Config) when is_list(Config) -> D1 = "cslab.ericsson.net", D2 = "mail1.cslab.ericsson.net", @@ -555,10 +564,7 @@ txt_record(Config) when is_list(Config) -> %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -files_monitor(suite) -> - []; -files_monitor(doc) -> - ["Tests monitoring of /etc/hosts and /etc/resolv.conf, but not them"]; +%% Tests monitoring of /etc/hosts and /etc/resolv.conf, but not them. files_monitor(Config) when is_list(Config) -> Search = inet_db:res_option(search), HostsFile = inet_db:res_option(hosts_file), @@ -573,7 +579,7 @@ files_monitor(Config) when is_list(Config) -> end. do_files_monitor(Config) -> - Dir = ?config(priv_dir, Config), + Dir = proplists:get_value(priv_dir, Config), {ok,Hostname} = inet:gethostname(), io:format("Hostname = ~p.~n", [Hostname]), FQDN = @@ -647,8 +653,7 @@ do_files_monitor(Config) -> %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -last_ms_answer(doc) -> - ["Answer just when timeout is triggered (OTP-9221)"]; +%% Answer just when timeout is triggered (OTP-9221). last_ms_answer(Config) when is_list(Config) -> NS = ns(Config), Name = "ns.otptest", diff --git a/lib/kernel/test/inet_res_SUITE_data/run-named b/lib/kernel/test/inet_res_SUITE_data/run-named index 211d2c7af7..d67295773a 100755 --- a/lib/kernel/test/inet_res_SUITE_data/run-named +++ b/lib/kernel/test/inet_res_SUITE_data/run-named @@ -2,18 +2,19 @@ ## ## %CopyrightBegin% ## -## Copyright Ericsson AB 2009-2012. All Rights Reserved. +## Copyright Ericsson AB 2009-2016. 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. +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. ## ## %CopyrightEnd% ## diff --git a/lib/kernel/test/inet_sockopt_SUITE.erl b/lib/kernel/test/inet_sockopt_SUITE.erl index 2b1abeb88f..ada9c2689c 100644 --- a/lib/kernel/test/inet_sockopt_SUITE.erl +++ b/lib/kernel/test/inet_sockopt_SUITE.erl @@ -1,24 +1,25 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2015. All Rights Reserved. +%% Copyright Ericsson AB 2007-2017. 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% -module(inet_sockopt_SUITE). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -define(C_GET_IPPROTO_TCP,1). @@ -61,7 +62,9 @@ -export([init_per_testcase/2, end_per_testcase/2]). -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap,{minutes,1}}]. all() -> [simple, loop_all, simple_raw, simple_raw_getbin, @@ -89,97 +92,91 @@ end_per_group(_GroupName, Config) -> init_per_testcase(_Func, Config) -> - Dog = test_server:timetrap(test_server:seconds(60)), - [{watchdog,Dog}|Config]. + Config. -end_per_testcase(_Func, Config) -> - Dog = ?config(watchdog, Config), - test_server:timetrap_cancel(Dog). +end_per_testcase(_Func, _Config) -> + ok. -simple(suite) -> []; -simple(doc) -> "Test inet:setopt/getopt simple functionality."; +%% Test inet:setopt/getopt simple functionality. simple(Config) when is_list(Config) -> - ?line XOpt = case os:type() of - {unix,_} -> [{reuseaddr,true}]; - _ -> [] - end, - ?line Opt = [{nodelay,true}, - {keepalive,true},{packet,4}, - {active,false}|XOpt], - ?line OptTags = [X || {X,_} <- Opt], - ?line {S1,S2} = create_socketpair(Opt, Opt), - ?line {ok,Opt} = inet:getopts(S1,OptTags), - ?line {ok,Opt} = inet:getopts(S2,OptTags), - ?line COpt = [{X,case X of nodelay -> false;_ -> Y end} || {X,Y} <- Opt], - ?line inet:setopts(S1,COpt), - ?line {ok,COpt} = inet:getopts(S1,OptTags), - ?line {ok,Opt} = inet:getopts(S2,OptTags), - ?line gen_tcp:close(S1), - ?line gen_tcp:close(S2), + XOpt = case os:type() of + {unix,_} -> [{reuseaddr,true}]; + _ -> [] + end, + Opt = [{nodelay,true}, + {keepalive,true},{packet,4}, + {active,false}|XOpt], + OptTags = [X || {X,_} <- Opt], + {S1,S2} = create_socketpair(Opt, Opt), + {ok,Opt} = inet:getopts(S1,OptTags), + {ok,Opt} = inet:getopts(S2,OptTags), + COpt = [{X,case X of nodelay -> false;_ -> Y end} || {X,Y} <- Opt], + inet:setopts(S1,COpt), + {ok,COpt} = inet:getopts(S1,OptTags), + {ok,Opt} = inet:getopts(S2,OptTags), + gen_tcp:close(S1), + gen_tcp:close(S2), ok. -loop_all(suite) -> []; -loop_all(doc) -> "Loop through all socket options and check that they work"; +%% Loop through all socket options and check that they work. loop_all(Config) when is_list(Config) -> - ?line ListenFailures = + ListenFailures = lists:foldr(make_check_fun(listen,1),[],all_listen_options()), - ?line ConnectFailures = + ConnectFailures = lists:foldr(make_check_fun(connect,2),[],all_connect_options()), - ?line case ListenFailures++ConnectFailures of - [] -> - ?line ok; - Failed -> - ?line {comment,lists:flatten( - io_lib:format("Non mandatory failed:~w", - [Failed]))} - end. + case ListenFailures++ConnectFailures of + [] -> + ok; + Failed -> + {comment,lists:flatten( + io_lib:format("Non mandatory failed:~w", + [Failed]))} + end. -simple_raw(suite) -> []; -simple_raw(doc) -> "Test simple setopt/getopt of raw options."; +%% Test simple setopt/getopt of raw options. simple_raw(Config) when is_list(Config) -> do_simple_raw(Config,false). -simple_raw_getbin(suite) -> []; -simple_raw_getbin(doc) -> "Test simple setopt/getopt of raw options, " - "with binaries in getopt."; + +%% Test simple setopt/getopt of raw options, with binaries in getopt. simple_raw_getbin(Config) when is_list(Config) -> do_simple_raw(Config,true). do_simple_raw(Config,Binary) when is_list(Config) -> - ?line Port = start_helper(Config), - ?line SolSocket = ask_helper(Port,?C_GET_SOL_SOCKET), - ?line SoKeepAlive = ask_helper(Port,?C_GET_SO_KEEPALIVE), - ?line OptionTrue = {raw,SolSocket,SoKeepAlive,<<1:32/native>>}, - ?line OptionFalse = {raw,SolSocket,SoKeepAlive,<<0:32/native>>}, - ?line {S1,S2} = create_socketpair([OptionTrue],[{keepalive,true}]), - ?line {ok,[{keepalive,true}]} = inet:getopts(S1,[keepalive]), - ?line {ok,[{keepalive,true}]} = inet:getopts(S2,[keepalive]), - ?line {ok,[{raw,SolSocket,SoKeepAlive,X1B}]} = + Port = start_helper(Config), + SolSocket = ask_helper(Port,?C_GET_SOL_SOCKET), + SoKeepAlive = ask_helper(Port,?C_GET_SO_KEEPALIVE), + OptionTrue = {raw,SolSocket,SoKeepAlive,<<1:32/native>>}, + OptionFalse = {raw,SolSocket,SoKeepAlive,<<0:32/native>>}, + {S1,S2} = create_socketpair([OptionTrue],[{keepalive,true}]), + {ok,[{keepalive,true}]} = inet:getopts(S1,[keepalive]), + {ok,[{keepalive,true}]} = inet:getopts(S2,[keepalive]), + {ok,[{raw,SolSocket,SoKeepAlive,X1B}]} = inet:getopts(S1,[{raw,SolSocket,SoKeepAlive,binarify(4,Binary)}]), - ?line X1 = nintbin2int(X1B), - ?line {ok,[{raw,SolSocket,SoKeepAlive,X2B}]} = + X1 = nintbin2int(X1B), + {ok,[{raw,SolSocket,SoKeepAlive,X2B}]} = inet:getopts(S2,[{raw,SolSocket,SoKeepAlive,binarify(4,Binary)}]), - ?line X2 = nintbin2int(X2B), - ?line true = X1 > 0, - ?line true = X2 > 0, - ?line inet:setopts(S1,[{keepalive,false}]), - ?line inet:setopts(S2,[OptionFalse]), - ?line {ok,[{keepalive,false}]} = inet:getopts(S1,[keepalive]), - ?line {ok,[{keepalive,false}]} = inet:getopts(S2,[keepalive]), - ?line {ok,[{raw,SolSocket,SoKeepAlive,Y1B}]} = + X2 = nintbin2int(X2B), + true = X1 > 0, + true = X2 > 0, + inet:setopts(S1,[{keepalive,false}]), + inet:setopts(S2,[OptionFalse]), + {ok,[{keepalive,false}]} = inet:getopts(S1,[keepalive]), + {ok,[{keepalive,false}]} = inet:getopts(S2,[keepalive]), + {ok,[{raw,SolSocket,SoKeepAlive,Y1B}]} = inet:getopts(S1,[{raw,SolSocket,SoKeepAlive,binarify(4,Binary)}]), - ?line Y1 = nintbin2int(Y1B), - ?line {ok,[{raw,SolSocket,SoKeepAlive,Y2B}]} = + Y1 = nintbin2int(Y1B), + {ok,[{raw,SolSocket,SoKeepAlive,Y2B}]} = inet:getopts(S2,[{raw,SolSocket,SoKeepAlive,binarify(4,Binary)}]), - ?line Y2 = nintbin2int(Y2B), - ?line true = Y1 == 0, - ?line true = Y2 == 0, - ?line gen_tcp:close(S1), - ?line gen_tcp:close(S2), - ?line stop_helper(Port), + Y2 = nintbin2int(Y2B), + true = Y1 == 0, + true = Y2 == 0, + gen_tcp:close(S1), + gen_tcp:close(S2), + stop_helper(Port), ok. - + nintbin2int(<<Int:32/native>>) -> Int; nintbin2int(<<Int:24/native>>) -> Int; nintbin2int(<<Int:16/native>>) -> Int; @@ -188,13 +185,12 @@ nintbin2int(<<>>) -> 0. -multiple_raw(suite) -> []; -multiple_raw(doc) -> "Test setopt/getopt of multiple raw options."; +%% Test setopt/getopt of multiple raw options. multiple_raw(Config) when is_list(Config) -> do_multiple_raw(Config,false). -multiple_raw_getbin(suite) -> []; -multiple_raw_getbin(doc) -> "Test setopt/getopt of multiple raw options, " - "with binaries in getopt."; + +%% Test setopt/getopt of multiple raw options, with binaries in +%% getopt. multiple_raw_getbin(Config) when is_list(Config) -> do_multiple_raw(Config,true). @@ -264,145 +260,143 @@ do_multiple_raw(Config, Binary) -> -doc_examples_raw(suite) -> []; -doc_examples_raw(doc) -> "Test that the example code from the documentation " - "works"; +%% Test that the example code from the documentation works. doc_examples_raw(Config) when is_list(Config) -> do_doc_examples_raw(Config,false). -doc_examples_raw_getbin(suite) -> []; -doc_examples_raw_getbin(doc) -> "Test that the example code from the " - "documentation works when getopt uses " - "binaries"; + +%% Test that the example code from the documentation works when getopt +%% uses binaries. doc_examples_raw_getbin(Config) when is_list(Config) -> do_doc_examples_raw(Config,true). + do_doc_examples_raw(Config,Binary) when is_list(Config) -> - ?line Port = start_helper(Config), - ?line Proto = ask_helper(Port,?C_GET_IPPROTO_TCP), - ?line TcpInfo = ask_helper(Port,?C_GET_TCP_INFO), - ?line TcpInfoSize = ask_helper(Port,?C_GET_TCP_INFO_SIZE), - ?line TcpiSackedOffset = ask_helper(Port,?C_GET_OFF_TCPI_SACKED), - ?line TcpiOptionsOffset = ask_helper(Port,?C_GET_OFF_TCPI_OPTIONS), - ?line TcpiSackedSize = ask_helper(Port,?C_GET_SIZ_TCPI_SACKED), - ?line TcpiOptionsSize = ask_helper(Port,?C_GET_SIZ_TCPI_OPTIONS), - ?line TcpLinger2 = ask_helper(Port,?C_GET_TCP_LINGER2), - ?line stop_helper(Port), + Port = start_helper(Config), + Proto = ask_helper(Port,?C_GET_IPPROTO_TCP), + TcpInfo = ask_helper(Port,?C_GET_TCP_INFO), + TcpInfoSize = ask_helper(Port,?C_GET_TCP_INFO_SIZE), + TcpiSackedOffset = ask_helper(Port,?C_GET_OFF_TCPI_SACKED), + TcpiOptionsOffset = ask_helper(Port,?C_GET_OFF_TCPI_OPTIONS), + TcpiSackedSize = ask_helper(Port,?C_GET_SIZ_TCPI_SACKED), + TcpiOptionsSize = ask_helper(Port,?C_GET_SIZ_TCPI_OPTIONS), + TcpLinger2 = ask_helper(Port,?C_GET_TCP_LINGER2), + stop_helper(Port), case all_ok([Proto,TcpInfo,TcpInfoSize,TcpiSackedOffset, TcpiOptionsOffset,TcpiSackedSize,TcpiOptionsSize, TcpLinger2]) of false -> {skipped,"Does not run on this OS."}; true -> - ?line {Sock,I} = create_socketpair([],[]), - ?line {ok,[{raw,Proto,TcpLinger2,<<OrigLinger:32/native>>}]} = + {Sock,I} = create_socketpair([],[]), + {ok,[{raw,Proto,TcpLinger2,<<OrigLinger:32/native>>}]} = inet:getopts(Sock,[{raw,Proto,TcpLinger2,binarify(4,Binary)}]), - ?line NewLinger = OrigLinger div 2, - ?line ok = inet:setopts(Sock,[{raw,Proto,TcpLinger2, - <<NewLinger:32/native>>}]), - ?line {ok,[{raw,Proto,TcpLinger2,<<NewLinger:32/native>>}]} = + NewLinger = OrigLinger div 2, + ok = inet:setopts(Sock,[{raw,Proto,TcpLinger2, + <<NewLinger:32/native>>}]), + {ok,[{raw,Proto,TcpLinger2,<<NewLinger:32/native>>}]} = inet:getopts(Sock,[{raw,Proto,TcpLinger2,binarify(4,Binary)}]), - ?line ok = inet:setopts(Sock,[{raw,Proto,TcpLinger2, - <<OrigLinger:32/native>>}]), - ?line {ok,[{raw,Proto,TcpLinger2,<<OrigLinger:32/native>>}]} = + ok = inet:setopts(Sock,[{raw,Proto,TcpLinger2, + <<OrigLinger:32/native>>}]), + {ok,[{raw,Proto,TcpLinger2,<<OrigLinger:32/native>>}]} = inet:getopts(Sock,[{raw,Proto,TcpLinger2,binarify(4,Binary)}]), - ?line {ok,[{raw,_,_,Info}]} = + {ok,[{raw,_,_,Info}]} = inet:getopts(Sock,[{raw,Proto,TcpInfo, binarify(TcpInfoSize,Binary)}]), - ?line Bit1 = TcpiSackedSize * 8, - ?line <<_:TcpiSackedOffset/binary, - TcpiSacked:Bit1/native,_/binary>> = + Bit1 = TcpiSackedSize * 8, + <<_:TcpiSackedOffset/binary, + TcpiSacked:Bit1/native,_/binary>> = Info, - ?line 0 = TcpiSacked, - ?line Bit2 = TcpiOptionsSize * 8, - ?line <<_:TcpiOptionsOffset/binary, - TcpiOptions:Bit2/native,_/binary>> = + 0 = TcpiSacked, + Bit2 = TcpiOptionsSize * 8, + <<_:TcpiOptionsOffset/binary, + TcpiOptions:Bit2/native,_/binary>> = Info, - ?line true = TcpiOptions =/= 0, - ?line gen_tcp:close(Sock), - ?line gen_tcp:close(I), + true = TcpiOptions =/= 0, + gen_tcp:close(Sock), + gen_tcp:close(I), ok end. - -large_raw(suite) -> []; -large_raw(doc) -> "Test structs and large/too large buffers when raw"; + +%% Test structs and large/too large buffers when raw. large_raw(Config) when is_list(Config) -> do_large_raw(Config,false). -large_raw_getbin(suite) -> []; -large_raw_getbin(doc) -> "Test structs and large/too large buffers when raw" - "using binaries to getopts"; + +%% Test structs and large/too large buffers when raw +%% using binaries to getopts. large_raw_getbin(Config) when is_list(Config) -> do_large_raw(Config,true). + do_large_raw(Config,Binary) when is_list(Config) -> - ?line Port = start_helper(Config), - ?line Proto = ask_helper(Port,?C_GET_SOL_SOCKET), - ?line Linger = ask_helper(Port,?C_GET_SO_LINGER), - ?line LingerSize = ask_helper(Port,?C_GET_LINGER_SIZE), - ?line LingerOnOffOffset = ask_helper(Port,?C_GET_OFF_LINGER_L_ONOFF), - ?line LingerLingerOffset = ask_helper(Port,?C_GET_OFF_LINGER_L_LINGER), - ?line LingerOnOffSize = ask_helper(Port,?C_GET_SIZ_LINGER_L_ONOFF), - ?line LingerLingerSize = ask_helper(Port,?C_GET_SIZ_LINGER_L_LINGER), - ?line stop_helper(Port), + Port = start_helper(Config), + Proto = ask_helper(Port,?C_GET_SOL_SOCKET), + Linger = ask_helper(Port,?C_GET_SO_LINGER), + LingerSize = ask_helper(Port,?C_GET_LINGER_SIZE), + LingerOnOffOffset = ask_helper(Port,?C_GET_OFF_LINGER_L_ONOFF), + LingerLingerOffset = ask_helper(Port,?C_GET_OFF_LINGER_L_LINGER), + LingerOnOffSize = ask_helper(Port,?C_GET_SIZ_LINGER_L_ONOFF), + LingerLingerSize = ask_helper(Port,?C_GET_SIZ_LINGER_L_LINGER), + stop_helper(Port), case all_ok([Proto,Linger,LingerSize,LingerOnOffOffset, LingerLingerOffset,LingerOnOffSize,LingerLingerSize]) of false -> {skipped,"Does not run on this OS."}; true -> - ?line {Sock1,Sock2} = create_socketpair([{linger,{true,10}}], - [{linger,{false,0}}]), - ?line LargeSize = 1024, % Solaris can take up to 1024*9, - % linux 1024*63... - ?line TooLargeSize = 1024*64, - ?line {ok,[{raw,Proto,Linger,Linger1}]} = + {Sock1,Sock2} = create_socketpair([{linger,{true,10}}], + [{linger,{false,0}}]), + LargeSize = 1024, % Solaris can take up to 1024*9, + % linux 1024*63... + TooLargeSize = 1024*64, + {ok,[{raw,Proto,Linger,Linger1}]} = inet:getopts(Sock1,[{raw,Proto,Linger, binarify(LargeSize,Binary)}]), - ?line {ok,[{raw,Proto,Linger,Linger2}]} = + {ok,[{raw,Proto,Linger,Linger2}]} = inet:getopts(Sock2,[{raw,Proto,Linger, binarify(LingerSize,Binary)}]), - ?line true = byte_size(Linger1) =:= LingerSize, - ?line LingerLingerBits = LingerLingerSize * 8, - ?line LingerOnOffBits = LingerOnOffSize * 8, - ?line <<_:LingerLingerOffset/binary, - Ling1:LingerLingerBits/native,_/binary>> = Linger1, - ?line <<_:LingerOnOffOffset/binary, - Off1:LingerOnOffBits/native,_/binary>> = Linger1, - ?line <<_:LingerOnOffOffset/binary, - Off2:LingerOnOffBits/native,_/binary>> = Linger2, - ?line true = Off1 =/= 0, - ?line true = Off2 == 0, - ?line true = Ling1 == 10, - ?line {error,einval} = + true = byte_size(Linger1) =:= LingerSize, + LingerLingerBits = LingerLingerSize * 8, + LingerOnOffBits = LingerOnOffSize * 8, + <<_:LingerLingerOffset/binary, + Ling1:LingerLingerBits/native,_/binary>> = Linger1, + <<_:LingerOnOffOffset/binary, + Off1:LingerOnOffBits/native,_/binary>> = Linger1, + <<_:LingerOnOffOffset/binary, + Off2:LingerOnOffBits/native,_/binary>> = Linger2, + true = Off1 =/= 0, + true = Off2 == 0, + true = Ling1 == 10, + {error,einval} = inet:getopts(Sock1,[{raw,Proto,Linger,TooLargeSize}]), - ?line gen_tcp:close(Sock1), - ?line gen_tcp:close(Sock2), + gen_tcp:close(Sock1), + gen_tcp:close(Sock2), ok end. -combined(suite) -> []; -combined(doc) -> "Test raw structs combined w/ other options "; +%% Test raw structs combined w/ other options . combined(Config) when is_list(Config) -> do_combined(Config,false). -combined_getbin(suite) -> []; -combined_getbin(doc) -> "Test raw structs combined w/ other options and " - "binarise in getopts"; + +%% Test raw structs combined w/ other options and +%% binarise in getopts. combined_getbin(Config) when is_list(Config) -> do_combined(Config,true). + do_combined(Config,Binary) when is_list(Config) -> - ?line Port = start_helper(Config), - ?line Proto = ask_helper(Port,?C_GET_SOL_SOCKET), - ?line Linger = ask_helper(Port,?C_GET_SO_LINGER), - ?line LingerSize = ask_helper(Port,?C_GET_LINGER_SIZE), - ?line LingerOnOffOffset = ask_helper(Port,?C_GET_OFF_LINGER_L_ONOFF), - ?line LingerLingerOffset = ask_helper(Port,?C_GET_OFF_LINGER_L_LINGER), - ?line LingerOnOffSize = ask_helper(Port,?C_GET_SIZ_LINGER_L_ONOFF), - ?line LingerLingerSize = ask_helper(Port,?C_GET_SIZ_LINGER_L_LINGER), - ?line stop_helper(Port), + Port = start_helper(Config), + Proto = ask_helper(Port,?C_GET_SOL_SOCKET), + Linger = ask_helper(Port,?C_GET_SO_LINGER), + LingerSize = ask_helper(Port,?C_GET_LINGER_SIZE), + LingerOnOffOffset = ask_helper(Port,?C_GET_OFF_LINGER_L_ONOFF), + LingerLingerOffset = ask_helper(Port,?C_GET_OFF_LINGER_L_LINGER), + LingerOnOffSize = ask_helper(Port,?C_GET_SIZ_LINGER_L_ONOFF), + LingerLingerSize = ask_helper(Port,?C_GET_SIZ_LINGER_L_LINGER), + stop_helper(Port), case all_ok([Proto,Linger,LingerSize,LingerOnOffOffset, LingerLingerOffset,LingerOnOffSize,LingerLingerSize]) of false -> {skipped,"Does not run on this OS."}; true -> - ?line LingerLingerBits = LingerLingerSize * 8, - ?line LingerOnOffBits = LingerOnOffSize * 8, - ?line {LingerOn,LingerOff} = + LingerLingerBits = LingerLingerSize * 8, + LingerOnOffBits = LingerOnOffSize * 8, + {LingerOn,LingerOff} = case LingerOnOffOffset < LingerLingerOffset of true -> Pad1 = @@ -422,11 +416,11 @@ do_combined(Config,Binary) when is_list(Config) -> lists:duplicate(Pad3Siz, 0)), {<<Pad1/binary,1:LingerOnOffBits/native, - Pad2/binary,10:LingerLingerBits/native, - Pad3/binary>>, + Pad2/binary,10:LingerLingerBits/native, + Pad3/binary>>, <<Pad1/binary,0:LingerOnOffBits/native, - Pad2/binary,0:LingerLingerBits/native, - Pad3/binary>>}; + Pad2/binary,0:LingerLingerBits/native, + Pad3/binary>>}; false -> Pad1 = list_to_binary( @@ -445,177 +439,174 @@ do_combined(Config,Binary) when is_list(Config) -> lists:duplicate(Pad3Siz, 0)), {<<Pad1/binary,1:LingerLingerBits/native, - Pad2/binary,10:LingerOnOffBits/native, - Pad3/binary>>, + Pad2/binary,10:LingerOnOffBits/native, + Pad3/binary>>, <<Pad1/binary,0:LingerLingerBits/native, - Pad2/binary,0:LingerOnOffBits/native, - Pad3/binary>>} + Pad2/binary,0:LingerOnOffBits/native, + Pad3/binary>>} end, - ?line RawLingerOn = {raw,Proto,Linger,LingerOn}, - ?line RawLingerOff = {raw,Proto,Linger,LingerOff}, - ?line {Sock1,Sock2} = + RawLingerOn = {raw,Proto,Linger,LingerOn}, + RawLingerOff = {raw,Proto,Linger,LingerOff}, + {Sock1,Sock2} = create_socketpair([{keepalive,true}, RawLingerOn], [{keepalive,false}, RawLingerOff]), - ?line {ok,[{raw,Proto,Linger,Linger1},{keepalive,Keep1}]} = + {ok,[{raw,Proto,Linger,Linger1},{keepalive,Keep1}]} = inet:getopts(Sock1,[{raw,Proto,Linger, binarify(LingerSize,Binary)},keepalive]), - ?line {ok,[{raw,Proto,Linger,Linger2},{keepalive,Keep2}]} = + {ok,[{raw,Proto,Linger,Linger2},{keepalive,Keep2}]} = inet:getopts(Sock2,[{raw,Proto,Linger, binarify(LingerSize,Binary)},keepalive]), - ?line true = byte_size(Linger1) =:= LingerSize, - ?line <<_:LingerLingerOffset/binary, - Ling1:LingerLingerBits/native,_/binary>> = Linger1, - ?line <<_:LingerOnOffOffset/binary, - Off1:LingerOnOffBits/native,_/binary>> = Linger1, - ?line <<_:LingerOnOffOffset/binary, - Off2:LingerOnOffBits/native,_/binary>> = Linger2, - ?line true = Off1 =/= 0, - ?line true = Off2 == 0, - ?line true = Ling1 == 10, - ?line true = Keep1 =:= true, - ?line true = Keep2 =:= false, - ?line {Sock3,Sock4} = + true = byte_size(Linger1) =:= LingerSize, + <<_:LingerLingerOffset/binary, + Ling1:LingerLingerBits/native,_/binary>> = Linger1, + <<_:LingerOnOffOffset/binary, + Off1:LingerOnOffBits/native,_/binary>> = Linger1, + <<_:LingerOnOffOffset/binary, + Off2:LingerOnOffBits/native,_/binary>> = Linger2, + true = Off1 =/= 0, + true = Off2 == 0, + true = Ling1 == 10, + true = Keep1 =:= true, + true = Keep2 =:= false, + {Sock3,Sock4} = create_socketpair([RawLingerOn,{keepalive,true}], [RawLingerOff,{keepalive,false}]), - ?line {ok,[{raw,Proto,Linger,Linger3},{keepalive,Keep3}]} = + {ok,[{raw,Proto,Linger,Linger3},{keepalive,Keep3}]} = inet:getopts(Sock3,[{raw,Proto,Linger, binarify(LingerSize,Binary)},keepalive]), - ?line {ok,[{raw,Proto,Linger,Linger4},{keepalive,Keep4}]} = + {ok,[{raw,Proto,Linger,Linger4},{keepalive,Keep4}]} = inet:getopts(Sock4,[{raw,Proto,Linger, binarify(LingerSize,Binary)},keepalive]), - ?line true = byte_size(Linger3) =:= LingerSize, - ?line <<_:LingerLingerOffset/binary, - Ling3:LingerLingerBits/native,_/binary>> = Linger3, - ?line <<_:LingerOnOffOffset/binary, - Off3:LingerOnOffBits/native,_/binary>> = Linger3, - ?line <<_:LingerOnOffOffset/binary, - Off4:LingerOnOffBits/native,_/binary>> = Linger4, - ?line true = Off3 =/= 0, - ?line true = Off4 == 0, - ?line true = Ling3 == 10, - ?line true = Keep3 =:= true, - ?line true = Keep4 =:= false, - ?line {Sock5,Sock6} = + true = byte_size(Linger3) =:= LingerSize, + <<_:LingerLingerOffset/binary, + Ling3:LingerLingerBits/native,_/binary>> = Linger3, + <<_:LingerOnOffOffset/binary, + Off3:LingerOnOffBits/native,_/binary>> = Linger3, + <<_:LingerOnOffOffset/binary, + Off4:LingerOnOffBits/native,_/binary>> = Linger4, + true = Off3 =/= 0, + true = Off4 == 0, + true = Ling3 == 10, + true = Keep3 =:= true, + true = Keep4 =:= false, + {Sock5,Sock6} = create_socketpair([{packet,4},RawLingerOn,{keepalive,true}], [{packet,2},RawLingerOff,{keepalive,false}]), - ?line {ok,[{packet,Pack5},{raw,Proto,Linger,Linger5}, - {keepalive,Keep5}]} = + {ok,[{packet,Pack5},{raw,Proto,Linger,Linger5}, + {keepalive,Keep5}]} = inet:getopts(Sock5,[packet,{raw,Proto,Linger, binarify(LingerSize,Binary)}, keepalive]), - ?line {ok,[{packet,Pack6},{raw,Proto,Linger,Linger6}, - {keepalive,Keep6}]} = + {ok,[{packet,Pack6},{raw,Proto,Linger,Linger6}, + {keepalive,Keep6}]} = inet:getopts(Sock6,[packet,{raw,Proto,Linger, binarify(LingerSize,Binary)}, keepalive]), - ?line true = byte_size(Linger5) =:= LingerSize, - ?line <<_:LingerLingerOffset/binary, - Ling5:LingerLingerBits/native,_/binary>> = Linger5, - ?line <<_:LingerOnOffOffset/binary, - Off5:LingerOnOffBits/native,_/binary>> = Linger5, - ?line <<_:LingerOnOffOffset/binary, - Off6:LingerOnOffBits/native,_/binary>> = Linger6, - ?line true = Off5 =/= 0, - ?line true = Off6 == 0, - ?line true = Ling5 == 10, - ?line true = Keep5 =:= true, - ?line true = Keep6 =:= false, - ?line true = Pack5 =:= 4, - ?line true = Pack6 =:= 2, - ?line inet:setopts(Sock6,[{packet,4},RawLingerOn, - {keepalive,true}]), - ?line {ok,[{packet,Pack7},{raw,Proto,Linger,Linger7}, - {keepalive,Keep7}]} = + true = byte_size(Linger5) =:= LingerSize, + <<_:LingerLingerOffset/binary, + Ling5:LingerLingerBits/native,_/binary>> = Linger5, + <<_:LingerOnOffOffset/binary, + Off5:LingerOnOffBits/native,_/binary>> = Linger5, + <<_:LingerOnOffOffset/binary, + Off6:LingerOnOffBits/native,_/binary>> = Linger6, + true = Off5 =/= 0, + true = Off6 == 0, + true = Ling5 == 10, + true = Keep5 =:= true, + true = Keep6 =:= false, + true = Pack5 =:= 4, + true = Pack6 =:= 2, + inet:setopts(Sock6,[{packet,4},RawLingerOn, + {keepalive,true}]), + {ok,[{packet,Pack7},{raw,Proto,Linger,Linger7}, + {keepalive,Keep7}]} = inet:getopts(Sock6,[packet,{raw,Proto,Linger, binarify(LingerSize,Binary)}, keepalive]), - ?line <<_:LingerOnOffOffset/binary, - Off7:LingerOnOffBits/native,_/binary>> = Linger7, - ?line true = Off7 =/= 0, - ?line true = Keep7 =:= true, - ?line true = Pack7 =:= 4, - ?line gen_tcp:close(Sock1), - ?line gen_tcp:close(Sock2), - ?line gen_tcp:close(Sock3), - ?line gen_tcp:close(Sock4), - ?line gen_tcp:close(Sock5), - ?line gen_tcp:close(Sock6), + <<_:LingerOnOffOffset/binary, + Off7:LingerOnOffBits/native,_/binary>> = Linger7, + true = Off7 =/= 0, + true = Keep7 =:= true, + true = Pack7 =:= 4, + gen_tcp:close(Sock1), + gen_tcp:close(Sock2), + gen_tcp:close(Sock3), + gen_tcp:close(Sock4), + gen_tcp:close(Sock5), + gen_tcp:close(Sock6), ok end. -ipv6_v6only_udp(suite) -> []; -ipv6_v6only_udp(doc) -> "Test socket option ipv6_v6only for UDP"; +%% Test socket option ipv6_v6only for UDP. ipv6_v6only_udp(Config) when is_list(Config) -> ipv6_v6only(Config, gen_udp). -ipv6_v6only_tcp(suite) -> []; -ipv6_v6only_tcp(doc) -> "Test socket option ipv6_v6only for TCP"; +%% Test socket option ipv6_v6only for TCP. ipv6_v6only_tcp(Config) when is_list(Config) -> ipv6_v6only(Config, gen_tcp). -ipv6_v6only_sctp(suite) -> []; -ipv6_v6only_sctp(doc) -> "Test socket option ipv6_v6only for SCTP"; +%% Test socket option ipv6_v6only for SCTP. ipv6_v6only_sctp(Config) when is_list(Config) -> ipv6_v6only(Config, gen_sctp). ipv6_v6only(Config, Module) when is_list(Config) -> - ?line case ipv6_v6only_open(Module, []) of - {ok,S1} -> - ?line case inet:getopts(S1, [ipv6_v6only]) of - {ok,[{ipv6_v6only,Default}]} - when is_boolean(Default) -> - ?line ok = - ipv6_v6only_close(Module, S1), - ?line ipv6_v6only(Config, Module, Default); - {ok,[]} -> - ?line io:format("Not implemented.~n", []), - %% This list of OS:es where the option is - %% supposed to be not implemented is just - %% a guess, and may grow with time. - ?line case {os:type(),os:version()} of - {{unix,linux},{2,M,_}} - when M =< 4 -> ok - end, - %% At least this should work - ?line {ok,S2} = - ipv6_v6only_open( - Module, - [{ipv6_v6only,true}]), - ?line ok = - ipv6_v6only_close(Module, S2) - end; - {error,_} -> - {skipped,"Socket type not supported"} - end. + case ipv6_v6only_open(Module, []) of + {ok,S1} -> + case inet:getopts(S1, [ipv6_v6only]) of + {ok,[{ipv6_v6only,Default}]} + when is_boolean(Default) -> + ok = + ipv6_v6only_close(Module, S1), + ipv6_v6only(Config, Module, Default); + {ok,[]} -> + io:format("Not implemented.~n", []), + %% This list of OS:es where the option is + %% supposed to be not implemented is just + %% a guess, and may grow with time. + case {os:type(),os:version()} of + {{unix,linux},{2,M,_}} + when M =< 4 -> ok + end, + %% At least this should work + {ok,S2} = + ipv6_v6only_open( + Module, + [{ipv6_v6only,true}]), + ok = + ipv6_v6only_close(Module, S2) + end; + {error,_} -> + {skipped,"Socket type not supported"} + end. ipv6_v6only(Config, Module, Default) when is_list(Config) -> - ?line io:format("Default ~w.~n", [Default]), - ?line {ok,S1} = + io:format("Default ~w.~n", [Default]), + {ok,S1} = ipv6_v6only_open(Module, [{ipv6_v6only,Default}]), - ?line {ok,[{ipv6_v6only,Default}]} = + {ok,[{ipv6_v6only,Default}]} = inet:getopts(S1, [ipv6_v6only]), - ?line ok = + ok = ipv6_v6only_close(Module, S1), - ?line NotDefault = not Default, - ?line case ipv6_v6only_open(Module, [{ipv6_v6only,NotDefault}]) of - {ok,S2} -> - ?line io:format("Read-write.~n", []), - ?line {ok,[{ipv6_v6only,NotDefault}]} = - inet:getopts(S2, [ipv6_v6only]), - ok; - {error,einval} -> - ?line io:format("Read-only.~n", []), - %% This option is known to be read-only and true - %% on Windows and OpenBSD - ?line case os:type() of - {unix,openbsd} when Default =:= true -> ok; - {win32,_} when Default =:= true -> ok - end - end. + NotDefault = not Default, + case ipv6_v6only_open(Module, [{ipv6_v6only,NotDefault}]) of + {ok,S2} -> + io:format("Read-write.~n", []), + {ok,[{ipv6_v6only,NotDefault}]} = + inet:getopts(S2, [ipv6_v6only]), + ok; + {error,einval} -> + io:format("Read-only.~n", []), + %% This option is known to be read-only and true + %% on Windows and OpenBSD + case os:type() of + {unix,openbsd} when Default =:= true -> ok; + {win32,_} when Default =:= true -> ok + end + end. ipv6_v6only_open(Module, Opts) -> Module:case Module of @@ -627,47 +618,46 @@ ipv6_v6only_close(Module, Socket) -> Module:close(Socket). -use_ipv6_v6only_udp(suite) -> []; -use_ipv6_v6only_udp(doc) -> "Test using socket option ipv6_v6only for UDP"; +%% Test using socket option ipv6_v6only for UDP. use_ipv6_v6only_udp(Config) when is_list(Config) -> - ?line case gen_udp:open(0, [inet6,{ipv6_v6only,true}]) of - {ok,S6} -> - ?line case inet:getopts(S6, [ipv6_v6only]) of - {ok,[{ipv6_v6only,true}]} -> - use_ipv6_v6only_udp(Config, S6); - {ok,Other} -> - {skipped,{getopts,Other}} - end; - {error,_} -> - {skipped,"Socket type not supported"} - end. + case gen_udp:open(0, [inet6,{ip,{0,0,0,0,0,0,0,1}}, {ipv6_v6only,true}]) of + {ok,S6} -> + case inet:getopts(S6, [ipv6_v6only]) of + {ok,[{ipv6_v6only,true}]} -> + use_ipv6_v6only_udp(Config, S6); + {ok,Other} -> + {skipped,{getopts,Other}} + end; + {error,_} -> + {skipped,"Socket type not supported"} + end. use_ipv6_v6only_udp(_Config, S6) -> - ?line {ok,Port} = inet:port(S6), - ?line {ok,S4} = gen_udp:open(Port, [inet]), - ?line E6 = " IPv6-echo.", - ?line E4 = " IPv4-echo.", - ?line Sender = + {ok,Port} = inet:port(S6), + {ok,S4} = gen_udp:open(Port, [inet]), + E6 = " IPv6-echo.", + E4 = " IPv4-echo.", + Sender = spawn_link(fun () -> use_ipv6_v6only_udp_sender(Port, E6, E4) end), - ?line use_ipv6_v6only_udp_listener( - S6, S4, E6, E4, monitor(process, Sender)). + use_ipv6_v6only_udp_listener( + S6, S4, E6, E4, monitor(process, Sender)). use_ipv6_v6only_udp_listener(S6, S4, E6, E4, Mref) -> - ?line receive - {udp,S6,IP,P,Data} -> - ?line ok = gen_udp:send(S6, IP, P, [Data|E6]), - ?line use_ipv6_v6only_udp_listener(S6, S4, E6, E4, Mref); - {udp,S4,IP,P,Data} -> - ?line ok = gen_udp:send(S4, IP, P, [Data|E4]), - ?line use_ipv6_v6only_udp_listener(S6, S4, E6, E4, Mref); - {'DOWN',Mref,_,_,normal} -> - ok; - {'DOWN',Mref,_,_,Result} -> - %% Since we are linked we will never arrive here - Result; - Other -> - ?line exit({failed,{listener_unexpected,Other}}) - end. + receive + {udp,S6,IP,P,Data} -> + ok = gen_udp:send(S6, IP, P, [Data|E6]), + use_ipv6_v6only_udp_listener(S6, S4, E6, E4, Mref); + {udp,S4,IP,P,Data} -> + ok = gen_udp:send(S4, IP, P, [Data|E4]), + use_ipv6_v6only_udp_listener(S6, S4, E6, E4, Mref); + {'DOWN',Mref,_,_,normal} -> + ok; + {'DOWN',Mref,_,_,Result} -> + %% Since we are linked we will never arrive here + Result; + Other -> + exit({failed,{listener_unexpected,Other}}) + end. use_ipv6_v6only_udp_sender(Port, E6, E4) -> D6 = "IPv6-send.", @@ -692,12 +682,9 @@ sndrcv(Ip, Port, Opts, Data) -> -type_errors(suite) -> - []; -type_errors(doc) -> - "Test that raw data requests are not executed for bad types"; +%% Test that raw data requests are not executed for bad types. type_errors(Config) when is_list(Config) -> - ?line BadSetOptions = + BadSetOptions = [ {raw,x,3,<<1:32>>}, {raw,1,tre,<<1:32>>}, @@ -715,7 +702,7 @@ type_errors(Config) when is_list(Config) -> rav, {linger,banan} ], - ?line BadGetOptions = + BadGetOptions = [ {raw,x,3,<<1:32>>}, {raw,1,tre,<<1:32>>}, @@ -734,46 +721,46 @@ type_errors(Config) when is_list(Config) -> rav, {linger,banan} ], - ?line lists:foreach(fun(Option) -> - ?line case - catch create_socketpair([Option],[]) of - {'EXIT',badarg} -> - ?line ok; - Unexpected1 -> - ?line exit({unexpected, - Unexpected1}) - end, - ?line case - catch create_socketpair([],[Option]) of - {'EXIT',badarg} -> - ?line ok; - Unexpected2 -> - ?line exit({unexpected, - Unexpected2}) - end, - ?line {Sock1,Sock2} = create_socketpair([],[]), - ?line case inet:setopts(Sock1, [Option]) of - {error,einval} -> - ?line ok; - Unexpected3 -> - ?line exit({unexpected, - Unexpected3}) - end, - ?line gen_tcp:close(Sock1), - ?line gen_tcp:close(Sock2) - end,BadSetOptions), - ?line {Sock1,Sock2} = create_socketpair([],[]), - ?line lists:foreach(fun(Option) -> - ?line case inet:getopts(Sock1, [Option]) of - {error,einval} -> - ?line ok; - Unexpected -> - ?line exit({unexpected, - Unexpected}) - end - end,BadGetOptions), - ?line gen_tcp:close(Sock1), - ?line gen_tcp:close(Sock2), + lists:foreach(fun(Option) -> + case + catch create_socketpair([Option],[]) of + {'EXIT',badarg} -> + ok; + Unexpected1 -> + exit({unexpected, + Unexpected1}) + end, + case + catch create_socketpair([],[Option]) of + {'EXIT',badarg} -> + ok; + Unexpected2 -> + exit({unexpected, + Unexpected2}) + end, + {Sock1,Sock2} = create_socketpair([],[]), + case inet:setopts(Sock1, [Option]) of + {error,einval} -> + ok; + Unexpected3 -> + exit({unexpected, + Unexpected3}) + end, + gen_tcp:close(Sock1), + gen_tcp:close(Sock2) + end,BadSetOptions), + {Sock1,Sock2} = create_socketpair([],[]), + lists:foreach(fun(Option) -> + case inet:getopts(Sock1, [Option]) of + {error,einval} -> + ok; + Unexpected -> + exit({unexpected, + Unexpected}) + end + end,BadGetOptions), + gen_tcp:close(Sock1), + gen_tcp:close(Sock2), ok. all_ok([]) -> @@ -783,59 +770,59 @@ all_ok([H|T]) when H >= 0 -> all_ok(_) -> false. - + make_check_fun(Type,Element) -> fun({Name,V1,V2,Mand,Chang},Acc) -> - ?line {LO1,CO1} = setelement(Element,{[],[]}, [{Name,V1}]), - ?line {LO2,CO2} = setelement(Element,{[],[]}, [{Name,V2}]), - ?line {X1,Y1} = create_socketpair(LO1,CO1), - ?line {X2,Y2} = create_socketpair(LO2,CO2), - ?line S1 = element(Element,{X1,Y1}), - ?line S2 = element(Element,{X2,Y2}), - ?line {ok,[{Name,R1}]} = inet:getopts(S1,[Name]), - ?line {ok,[{Name,R2}]} = inet:getopts(S2,[Name]), + {LO1,CO1} = setelement(Element,{[],[]}, [{Name,V1}]), + {LO2,CO2} = setelement(Element,{[],[]}, [{Name,V2}]), + {X1,Y1} = create_socketpair(LO1,CO1), + {X2,Y2} = create_socketpair(LO2,CO2), + S1 = element(Element,{X1,Y1}), + S2 = element(Element,{X2,Y2}), + {ok,[{Name,R1}]} = inet:getopts(S1,[Name]), + {ok,[{Name,R2}]} = inet:getopts(S2,[Name]), NewAcc = case R1 =/= R2 of true -> case Chang of true -> - ?line inet:setopts(S1,[{Name,V2}]), - ?line {ok,[{Name,R3}]} = + inet:setopts(S1,[{Name,V2}]), + {ok,[{Name,R3}]} = inet:getopts(S1,[Name]), case {R3 =/= R1, R3 =:= R2} of {true,true} -> - ?line Acc; + Acc; _ -> case Mand of true -> - ?line exit - ({failed_sockopt, - {change, - Name}}); + exit + ({failed_sockopt, + {change, + Name}}); false -> - ?line [{change,Name}|Acc] + [{change,Name}|Acc] end end; false -> - ?line Acc + Acc end; false -> case Mand of true -> - ?line exit({failed_sockopt, - {Type,Name}}); + exit({failed_sockopt, + {Type,Name}}); false -> - ?line [{Type,Name}|Acc] + [{Type,Name}|Acc] end end, - ?line gen_tcp:close(X1), - ?line gen_tcp:close(Y1), - ?line gen_tcp:close(X2), - ?line gen_tcp:close(Y2), + gen_tcp:close(X1), + gen_tcp:close(Y1), + gen_tcp:close(X2), + gen_tcp:close(Y2), NewAcc - end. + end. -% {OptionName,Value1,Value2,Mandatory,Changeable} +%% {OptionName,Value1,Value2,Mandatory,Changeable} all_listen_options() -> [{tos,0,1,false,true}, {priority,0,1,false,true}, @@ -886,19 +873,19 @@ all_connect_options() -> {delay_send,false,true,true,true}, {packet_size,0,4,true,true} ]. - + create_socketpair(ListenOptions,ConnectOptions) -> - ?line {ok,LS}=gen_tcp:listen(0,ListenOptions), - ?line {ok,Port}=inet:port(LS), - ?line {ok,CS}=gen_tcp:connect(localhost,Port,ConnectOptions), - ?line {ok,AS}=gen_tcp:accept(LS), - ?line gen_tcp:close(LS), + {ok,LS}=gen_tcp:listen(0,ListenOptions), + {ok,Port}=inet:port(LS), + {ok,CS}=gen_tcp:connect(localhost,Port,ConnectOptions), + {ok,AS}=gen_tcp:accept(LS), + gen_tcp:close(LS), {AS,CS}. start_helper(Config) -> - Progname = filename:join(?config(data_dir, Config), "sockopt_helper"), + Progname = filename:join(proplists:get_value(data_dir, Config), "sockopt_helper"), Port = open_port({spawn,Progname},[eof,line]), Port. diff --git a/lib/kernel/test/init_SUITE.erl b/lib/kernel/test/init_SUITE.erl index 6fe97ed04f..6a006cdc01 100644 --- a/lib/kernel/test/init_SUITE.erl +++ b/lib/kernel/test/init_SUITE.erl @@ -1,32 +1,34 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2012. All Rights Reserved. +%% Copyright Ericsson AB 1996-2018. 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/. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at %% -%% 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. +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% -module(init_SUITE). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2]). -export([get_arguments/1, get_argument/1, boot_var/1, restart/1, - many_restarts/1, + many_restarts/0, many_restarts/1, get_plain_arguments/1, - reboot/1, stop/1, get_status/1, script_id/1]). + reboot/1, stop_status/1, stop/1, get_status/1, script_id/1, + find_system_processes/0]). -export([boot1/1, boot2/1]). -export([init_per_testcase/2, end_per_testcase/2]). @@ -40,12 +42,14 @@ %% Should be started in a CC view with: %% erl -sname master -rsh ctrsh %%----------------------------------------------------------------- -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap,{minutes,2}}]. all() -> [get_arguments, get_argument, boot_var, many_restarts, - get_plain_arguments, restart, get_status, script_id, + get_plain_arguments, restart, stop_status, get_status, script_id, {group, boot}]. groups() -> @@ -64,46 +68,35 @@ end_per_group(_GroupName, Config) -> Config. -init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) -> - Dog=?t:timetrap(?t:seconds(?DEFAULT_TIMEOUT_SEC)), - [{watchdog, Dog}|Config]. +init_per_testcase(Func, Config) -> + Config. -end_per_testcase(_Func, Config) -> - Dog=?config(watchdog, Config), - ?t:timetrap_cancel(Dog). +end_per_testcase(_Func, _Config) -> + ok. -init(doc) -> []; -init(suite) -> []; init(Config) when is_list(Config) -> Config. -fini(doc) -> []; -fini(suite) -> []; fini(Config) when is_list(Config) -> Host = list_to_atom(from($@, atom_to_list(node()))), Node = list_to_atom(lists:concat([init_test, "@", Host])), stop_node(Node), Config. -get_arguments(doc) ->[]; -get_arguments(suite) -> {req, [distribution, {local_slave_nodes, 1}]}; get_arguments(Config) when is_list(Config) -> - ?line Dog = ?t:timetrap(?t:seconds(10)), - Args = args(), - ?line {ok, Node} = start_node(init_test, Args), - ?line case rpc:call(Node, init, get_arguments, []) of - Arguments when is_list(Arguments) -> - stop_node(Node), - check_a(Arguments), - check_b(Arguments), - check_c(Arguments), - check_d(Arguments); - _ -> - stop_node(Node), - ?t:fail(get_arguments) - end, - ?line ?t:timetrap_cancel(Dog), + {ok, Node} = start_node(init_test, Args), + case rpc:call(Node, init, get_arguments, []) of + Arguments when is_list(Arguments) -> + stop_node(Node), + check_a(Arguments), + check_b(Arguments), + check_c(Arguments), + check_d(Arguments); + _ -> + stop_node(Node), + ct:fail(get_arguments) + end, ok. check_a(Args) -> @@ -114,10 +107,10 @@ check_a(Args) -> false -> ok; _ -> - ?t:fail(check_a1) + ct:fail(check_a1) end; _ -> - ?t:fail(check_a2) + ct:fail(check_a2) end. check_b(Args) -> @@ -131,13 +124,13 @@ check_b(Args) -> false -> ok; _ -> - ?t:fail(check_b1) + ct:fail(check_b1) end; _ -> - ?t:fail(check_b2) + ct:fail(check_b2) end; _ -> - ?t:fail(check_b3) + ct:fail(check_b3) end. check_c(Args) -> @@ -151,13 +144,13 @@ check_c(Args) -> false -> ok; _ -> - ?t:fail(check_c1) + ct:fail(check_c1) end; _ -> - ?t:fail(check_c2) + ct:fail(check_c2) end; _ -> - ?t:fail(check_c3) + ct:fail(check_c3) end. check_d(Args) -> @@ -168,62 +161,54 @@ check_d(Args) -> false -> ok; _ -> - ?t:fail(check_d1) + ct:fail(check_d1) end; _ -> - ?t:fail(check_d2) + ct:fail(check_d2) end. -get_argument(doc) ->[]; -get_argument(suite) -> {req, [distribution, {local_slave_nodes, 1}]}; get_argument(Config) when is_list(Config) -> - ?line Dog = ?t:timetrap(?t:seconds(10)), - Args = args(), - ?line {ok, Node} = start_node(init_test, Args), - ?line case rpc:call(Node, init, get_argument, [b]) of - {ok, [["hej", "hopp"],["san", "sa"]]} -> - ok; - _ -> - stop_node(Node), - ?t:fail({get_argument, b}) - end, - ?line case rpc:call(Node, init, get_argument, [a]) of - {ok, [["kalle"]]} -> - ok; - _ -> - stop_node(Node), - ?t:fail({get_argument, a}) - end, - ?line case rpc:call(Node, init, get_argument, [c]) of - {ok, [["4", "5", "6"], ["7", "8", "9"]]} -> - ok; - _ -> - stop_node(Node), - ?t:fail({get_argument, c}) - end, - ?line case rpc:call(Node, init, get_argument, [d]) of - {ok, [[]]} -> - ok; - _ -> - stop_node(Node), - ?t:fail({get_argument, d}) - end, - ?line case rpc:call(Node, init, get_argument, [e]) of - error -> - ok; - _ -> - stop_node(Node), - ?t:fail({get_argument, e}) - end, + {ok, Node} = start_node(init_test, Args), + case rpc:call(Node, init, get_argument, [b]) of + {ok, [["hej", "hopp"],["san", "sa"]]} -> + ok; + _ -> + stop_node(Node), + ct:fail({get_argument, b}) + end, + case rpc:call(Node, init, get_argument, [a]) of + {ok, [["kalle"]]} -> + ok; + _ -> + stop_node(Node), + ct:fail({get_argument, a}) + end, + case rpc:call(Node, init, get_argument, [c]) of + {ok, [["4", "5", "6"], ["7", "8", "9"]]} -> + ok; + _ -> + stop_node(Node), + ct:fail({get_argument, c}) + end, + case rpc:call(Node, init, get_argument, [d]) of + {ok, [[]]} -> + ok; + _ -> + stop_node(Node), + ct:fail({get_argument, d}) + end, + case rpc:call(Node, init, get_argument, [e]) of + error -> + ok; + _ -> + stop_node(Node), + ct:fail({get_argument, e}) + end, stop_node(Node), - ?line ?t:timetrap_cancel(Dog), ok. -get_plain_arguments(doc) ->[]; -get_plain_arguments(suite) -> {req, [distribution, {local_slave_nodes, 1}]}; get_plain_arguments(Config) when is_list(Config) -> - ?line Dog = ?t:timetrap(?t:seconds(10)), Longstring = "fjdkfjdkfjfdaa2fjdkfjdkfjfdaa2fjdkfjdkfjfdaa2" "fjdkfjdkfjfdaa2fjdkfjdkfjfdaa2fjdkfjdkfjfdaa2" @@ -234,18 +219,17 @@ get_plain_arguments(Config) when is_list(Config) -> "fjdkfjdkfjfdaa2fjdkfjdkfjfdaa2fjdkfjdkfjfdaa2" "fjdkfjdkfjfdaa2fjdkfjdkfjfdaa2fjdkfjdkfjfdaa2" "fjdkfjdkfjfdaa2fjdkfjdkfjfdaa2fjdkfjdkfjfdaa2", - ?line true = (length(Longstring) > 255), + true = (length(Longstring) > 255), Args = long_args(Longstring), - ?line {ok, Node} = start_node(init_test, Args), - ?line case rpc:call(Node, init, get_plain_arguments, []) of - ["a", "b", "c", Longstring] -> - ok; - As -> - stop_node(Node), - ?t:fail({get_argument, As}) - end, + {ok, Node} = start_node(init_test, Args), + case rpc:call(Node, init, get_plain_arguments, []) of + ["a", "b", "c", Longstring] -> + ok; + As -> + stop_node(Node), + ct:fail({get_argument, As}) + end, stop_node(Node), - ?line ?t:timetrap_cancel(Dog), ok. @@ -253,199 +237,252 @@ get_plain_arguments(Config) when is_list(Config) -> %% ------------------------------------------------ %% Use -boot_var flag to set $TEST_VAR in boot script. %% ------------------------------------------------ -boot_var(doc) -> []; -boot_var(suite) -> {req, [distribution, {local_slave_nodes, 1}]}; boot_var(Config) when is_list(Config) -> - ?line Dog = ?t:timetrap(?t:seconds(100)), - {BootScript, TEST_VAR, KernelVsn, StdlibVsn} = create_boot(Config), %% Should fail as we have not given -boot_var TEST_VAR - ?line {error, timeout} = - start_node(init_test, "-boot " ++ BootScript), + {error, timeout} = + start_node(init_test, "-boot " ++ BootScript), case is_real_system(KernelVsn, StdlibVsn) of true -> %% Now it should work !! - ?line {ok, Node} = - start_node(init_test, - "-boot " ++ BootScript ++ - " -boot_var TEST_VAR " ++ TEST_VAR), + {ok, Node} = + start_node(init_test, + "-boot " ++ BootScript ++ + " -boot_var TEST_VAR \"" ++ + TEST_VAR ++ "\""), stop_node(Node), Res = ok; _ -> %% What we need is not so much version numbers on the directories, but %% for the boot var TEST_VAR to appear in the boot script, and it doesn't %% if we give the 'local' option to systools:make_script. - ?t:format( - "Test case not complete as we are not~n" - "running in a real system!~n" - "Probably this test is performed in a " - "clearcase view or source tree.~n" - "Need version numbers on the kernel and " - "stdlib directories!~n", - []), + io:format( + "Test case not complete as we are not~n" + "running in a real system!~n" + "Probably this test is performed in a " + "clearcase view or source tree.~n" + "Need version numbers on the kernel and " + "stdlib directories!~n", + []), Res = {skip, - "Test case only partially run since it is run " - "in a clearcase view or in a source tree. " - "Need an installed system to complete this test."} + "Test case only partially run since it is run " + "in a clearcase view or in a source tree. " + "Need an installed system to complete this test."} end, - ?line ?t:timetrap_cancel(Dog), Res. create_boot(Config) -> - ?line {ok, OldDir} = file:get_cwd(), - ?line {LatestDir, LatestName, KernelVsn, StdlibVsn} = + {ok, OldDir} = file:get_cwd(), + {LatestDir, LatestName, KernelVsn, StdlibVsn} = create_script(Config), LibDir = code:lib_dir(), - ?line ok = file:set_cwd(LatestDir), - ?line ok = systools:make_script(LatestName, - [{variables, [{"TEST_VAR", LibDir}]}]), - ?line ok = file:set_cwd(OldDir), + ok = file:set_cwd(LatestDir), + ok = systools:make_script(LatestName, + [{variables, [{"TEST_VAR", LibDir}]}]), + ok = file:set_cwd(OldDir), {LatestDir ++ "/" ++ LatestName, LibDir, KernelVsn, StdlibVsn}. is_real_system(KernelVsn, StdlibVsn) -> LibDir = code:lib_dir(), - filelib:is_dir(filename:join(LibDir, "kernel"++KernelVsn)) andalso - filelib:is_dir(filename:join(LibDir, "stdlib"++StdlibVsn)). - + filelib:is_dir(filename:join(LibDir, "kernel-"++KernelVsn)) andalso + filelib:is_dir(filename:join(LibDir, "stdlib-"++StdlibVsn)). + %% ------------------------------------------------ %% Slave executes erlang:halt() on master nodedown. %% Therefore the slave process must be killed %% before restart. %% ------------------------------------------------ -many_restarts(doc) -> []; -many_restarts(suite) -> - case ?t:os_type() of - {Fam, _} when Fam == unix; Fam == win32 -> - {req, [distribution, {local_slave_nodes, 1}, {time, 5}]}; - _ -> - {skip, "Only run on unix and win32"} - end; +many_restarts() -> + [{timetrap,{minutes,8}}]. many_restarts(Config) when is_list(Config) -> - ?line Dog = ?t:timetrap(?t:seconds(480)), - ?line {ok, Node} = loose_node:start(init_test, "", ?DEFAULT_TIMEOUT_SEC), - ?line loop_restart(30,Node,rpc:call(Node,erlang,whereis,[error_logger])), - ?line loose_node:stop(Node), - ?line ?t:timetrap_cancel(Dog), + {ok, Node} = loose_node:start(init_test, "", ?DEFAULT_TIMEOUT_SEC), + loop_restart(50,Node,rpc:call(Node,erlang,whereis,[logger])), + loose_node:stop(Node), ok. loop_restart(0,_,_) -> ok; loop_restart(N,Node,EHPid) -> - ?line erlang:monitor_node(Node, true), - ?line ok = rpc:call(Node, init, restart, []), - ?line receive - {nodedown, Node} -> - ok - after 10000 -> - loose_node:stop(Node), - ?t:fail(not_stopping) - end, - ?line ok = wait_for(30, Node, EHPid), - ?line loop_restart(N-1,Node,rpc:call(Node,erlang,whereis,[error_logger])). + erlang:monitor_node(Node, true), + ok = rpc:call(Node, init, restart, []), + receive + {nodedown, Node} -> + ok + after 10000 -> + loose_node:stop(Node), + ct:fail(not_stopping) + end, + ok = wait_for(30, Node, EHPid), + loop_restart(N-1,Node,rpc:call(Node,erlang,whereis,[logger])). wait_for(0,Node,_) -> loose_node:stop(Node), error; wait_for(N,Node,EHPid) -> - ?line case rpc:call(Node, erlang, whereis, [error_logger]) of + case rpc:call(Node, erlang, whereis, [logger]) of Pid when is_pid(Pid), Pid =/= EHPid -> - %% ?line erlang:display(ok), - ?line ok; + %% erlang:display(ok), + ok; _X -> - %% ?line erlang:display(_X), - %% ?line Procs = rpc:call(Node, erlang, processes, []), - %% ?line erlang:display(Procs), - %% case is_list(Procs) of - %% true -> - %% ?line [(catch erlang:display( - %% rpc:call(Node, - %% erlang, - %% process_info, - %% [Y,registered_name]))) - %% || Y <- Procs]; - %% _ -> - %% ok - %% end, - receive - after 100 -> - ok - end, - ?line wait_for(N-1,Node,EHPid) - end. + %% erlang:display(_X), + %% Procs = rpc:call(Node, erlang, processes, []), + %% erlang:display(Procs), + %% case is_list(Procs) of + %% true -> + %% [(catch erlang:display( + %% rpc:call(Node, + %% erlang, + %% process_info, + %% [Y,registered_name]))) + %% || Y <- Procs]; + %% _ -> + %% ok + %% end, + receive + after 100 -> + ok + end, + wait_for(N-1,Node,EHPid) + end. %% ------------------------------------------------ %% Slave executes erlang:halt() on master nodedown. %% Therefore the slave process must be killed %% before restart. %% ------------------------------------------------ -restart(doc) -> []; -restart(suite) -> - case ?t:os_type() of - {Fam, _} when Fam == unix; Fam == win32 -> - {req, [distribution, {local_slave_nodes, 1}, {time, 5}]}; - _ -> - {skip, "Only run on unix and win32"} - end; restart(Config) when is_list(Config) -> - ?line Dog = ?t:timetrap(?t:seconds(40)), - ?line Args = args(), + Args = args(), + + Pa = " -pa " ++ filename:dirname(code:which(?MODULE)), %% Currently test_server:start_node cannot be used. The restarted %% node immediately halts due to the implementation of %% test_server:start_node. - ?line {ok, Node} = loose_node:start(init_test, Args, ?DEFAULT_TIMEOUT_SEC), + {ok, Node} = loose_node:start(init_test, Args ++ Pa, ?DEFAULT_TIMEOUT_SEC), %% Ok, the node is up, now the real test test begins. - ?line erlang:monitor_node(Node, true), - ?line InitPid = rpc:call(Node, erlang, whereis, [init]), - ?line Procs = rpc:call(Node, erlang, processes, []), - ?line MaxPid = lists:last(Procs), - ?line ok = rpc:call(Node, init, restart, []), - ?line receive - {nodedown, Node} -> - ok - after 10000 -> - loose_node:stop(Node), - ?t:fail(not_stopping) - end, - ?line ok = wait_restart(30, Node), + erlang:monitor_node(Node, true), + SysProcs0 = rpc:call(Node, ?MODULE, find_system_processes, []), + io:format("SysProcs0=~p~n", [SysProcs0]), + [InitPid, PurgerPid, LitCollectorPid, + DirtySigNPid, DirtySigHPid, DirtySigMPid] = SysProcs0, + InitPid = rpc:call(Node, erlang, whereis, [init]), + PurgerPid = rpc:call(Node, erlang, whereis, [erts_code_purger]), + Procs = rpc:call(Node, erlang, processes, []), + MaxPid = lists:last(Procs), + ok = rpc:call(Node, init, restart, []), + receive + {nodedown, Node} -> + ok + after 10000 -> + loose_node:stop(Node), + ct:fail(not_stopping) + end, + ok = wait_restart(30, Node), + + SysProcs1 = rpc:call(Node, ?MODULE, find_system_processes, []), + io:format("SysProcs1=~p~n", [SysProcs1]), + [InitPid1, PurgerPid1, LitCollectorPid1, + DirtySigNPid1, DirtySigHPid1, DirtySigMPid1] = SysProcs1, %% Still the same init process! - ?line InitPid1 = rpc:call(Node, erlang, whereis, [init]), + InitPid1 = rpc:call(Node, erlang, whereis, [init]), InitP = pid_to_list(InitPid), - ?line InitP = pid_to_list(InitPid1), - - ?line NewProcs0 = rpc:call(Node, erlang, processes, []), - NewProcs = lists:delete(InitPid1, NewProcs0), - ?line case check_processes(NewProcs, MaxPid) of - true -> - ok; - _ -> - loose_node:stop(Node), - ?t:fail(processes_not_greater) - end, + InitP = pid_to_list(InitPid1), + + %% and same purger process! + PurgerPid1 = rpc:call(Node, erlang, whereis, [erts_code_purger]), + PurgerP = pid_to_list(PurgerPid), + PurgerP = pid_to_list(PurgerPid1), + + %% and same literal area collector process! + LitCollectorP = pid_to_list(LitCollectorPid), + LitCollectorP = pid_to_list(LitCollectorPid1), + + %% and same normal dirty signal handler process! + DirtySigNP = pid_to_list(DirtySigNPid), + DirtySigNP = pid_to_list(DirtySigNPid1), + %% and same high dirty signal handler process! + DirtySigHP = pid_to_list(DirtySigHPid), + DirtySigHP = pid_to_list(DirtySigHPid1), + %% and same max dirty signal handler process! + DirtySigMP = pid_to_list(DirtySigMPid), + DirtySigMP = pid_to_list(DirtySigMPid1), + + NewProcs0 = rpc:call(Node, erlang, processes, []), + NewProcs = NewProcs0 -- SysProcs1, + case check_processes(NewProcs, MaxPid) of + true -> + ok; + _ -> + loose_node:stop(Node), + ct:fail(processes_not_greater) + end, %% Test that, for instance, the same argument still exists. - ?line case rpc:call(Node, init, get_argument, [c]) of - {ok, [["4", "5", "6"], ["7", "8", "9"]]} -> - ok; - _ -> - loose_node:stop(Node), - ?t:fail({get_argument, restart_fail}) - end, + case rpc:call(Node, init, get_argument, [c]) of + {ok, [["4", "5", "6"], ["7", "8", "9"]]} -> + ok; + _ -> + loose_node:stop(Node), + ct:fail({get_argument, restart_fail}) + end, loose_node:stop(Node), - ?line ?t:timetrap_cancel(Dog), ok. +-record(sys_procs, {init, + code_purger, + literal_collector, + dirty_sig_handler_normal, + dirty_sig_handler_high, + dirty_sig_handler_max}). + +find_system_processes() -> + find_system_procs(processes(), #sys_procs{}). + +find_system_procs([], SysProcs) -> + [SysProcs#sys_procs.init, + SysProcs#sys_procs.code_purger, + SysProcs#sys_procs.literal_collector, + SysProcs#sys_procs.dirty_sig_handler_normal, + SysProcs#sys_procs.dirty_sig_handler_high, + SysProcs#sys_procs.dirty_sig_handler_max]; +find_system_procs([P|Ps], SysProcs) -> + case process_info(P, [initial_call, priority]) of + [{initial_call,{otp_ring0,start,2}},_] -> + undefined = SysProcs#sys_procs.init, + find_system_procs(Ps, SysProcs#sys_procs{init = P}); + [{initial_call,{erts_code_purger,start,0}},_] -> + undefined = SysProcs#sys_procs.code_purger, + find_system_procs(Ps, SysProcs#sys_procs{code_purger = P}); + [{initial_call,{erts_literal_area_collector,start,0}},_] -> + undefined = SysProcs#sys_procs.literal_collector, + find_system_procs(Ps, SysProcs#sys_procs{literal_collector = P}); + [{initial_call,{erts_dirty_process_signal_handler,start,0}}, + {priority,normal}] -> + undefined = SysProcs#sys_procs.dirty_sig_handler_normal, + find_system_procs(Ps, SysProcs#sys_procs{dirty_sig_handler_normal = P}); + [{initial_call,{erts_dirty_process_signal_handler,start,0}}, + {priority,high}] -> + undefined = SysProcs#sys_procs.dirty_sig_handler_high, + find_system_procs(Ps, SysProcs#sys_procs{dirty_sig_handler_high = P}); + [{initial_call,{erts_dirty_process_signal_handler,start,0}}, + {priority,max}] -> + undefined = SysProcs#sys_procs.dirty_sig_handler_max, + find_system_procs(Ps, SysProcs#sys_procs{dirty_sig_handler_max = P}); + _ -> + find_system_procs(Ps, SysProcs) + end. + wait_restart(0, _Node) -> - ?t:fail(not_restarted); + ct:fail(not_restarted); wait_restart(N, Node) -> case net_adm:ping(Node) of pong -> ok; _ -> - ?t:sleep(1000), + ct:sleep(1000), wait_restart(N - 1, Node) end. @@ -473,124 +510,112 @@ apid(Pid) -> %% The reboot facility using heart is tested %% in the heart_SUITE. %% ------------------------------------------------ -reboot(doc) -> []; -reboot(suite) -> {req, [distribution, {local_slave_nodes, 1}]}; reboot(Config) when is_list(Config) -> - ?line Dog = ?t:timetrap(?t:seconds(40)), - Args = args(), - ?line {ok, Node} = start_node(init_test, Args), + {ok, Node} = start_node(init_test, Args), erlang:monitor_node(Node, true), - ?line ok = rpc:call(Node, init, reboot, []), - ?line receive - {nodedown, Node} -> - ok - after 10000 -> - stop_node(Node), - ?t:fail(not_stopping) - end, - ?t:sleep(5000), - ?line case net_adm:ping(Node) of - pang -> - ok; - _ -> - stop_node(Node), - ?t:fail(system_rebooted) - end, - ?line ?t:timetrap_cancel(Dog), + ok = rpc:call(Node, init, reboot, []), + receive + {nodedown, Node} -> + ok + after 10000 -> + stop_node(Node), + ct:fail(not_stopping) + end, + ct:sleep(5000), + case net_adm:ping(Node) of + pang -> + ok; + _ -> + stop_node(Node), + ct:fail(system_rebooted) + end, ok. %% ------------------------------------------------ %% %% ------------------------------------------------ -stop(doc) -> []; -stop(suite) -> []; +stop_status(Config) when is_list(Config) -> + badarg = catch_stop([65,[66],67]), % flat strings only + badarg = catch_stop([65, 666, 67]), % only bytes in string + badarg = catch_stop(abort), % 'abort' not allowed + badarg = catch_stop(true), % other atoms not allowed + badarg = catch_stop(-1), % no negative statuses + ok. + +catch_stop(Status) -> + try init:stop(Status) catch error:badarg -> badarg end. + +%% ------------------------------------------------ +%% +%% ------------------------------------------------ stop(Config) when is_list(Config) -> - ?line Dog = ?t:timetrap(?t:seconds(20)), Args = args(), - ?line {ok, Node} = start_node(init_test, Args), + {ok, Node} = start_node(init_test, Args), erlang:monitor_node(Node, true), - ?line ok = rpc:call(Node, init, reboot, []), - ?line receive - {nodedown, Node} -> - ok - after 10000 -> - stop_node(Node), - ?t:fail(not_stopping) - end, - ?t:sleep(5000), - ?line case net_adm:ping(Node) of - pang -> - ok; - _ -> - stop_node(Node), - ?t:fail(system_rebooted) - end, - ?line ?t:timetrap_cancel(Dog), + ok = rpc:call(Node, init, reboot, []), + receive + {nodedown, Node} -> + ok + after 10000 -> + stop_node(Node), + ct:fail(not_stopping) + end, + ct:sleep(5000), + case net_adm:ping(Node) of + pang -> + ok; + _ -> + stop_node(Node), + ct:fail(system_rebooted) + end, ok. %% ------------------------------------------------ %% %% ------------------------------------------------ -get_status(doc) -> []; -get_status(suite) -> []; get_status(Config) when is_list(Config) -> - ?line Dog = ?t:timetrap(?t:seconds(10)), - ?line ?t:timetrap_cancel(Dog), + {Start, _} = init:get_status(), - ?line {Start, _} = init:get_status(), %% Depending on how the test_server is started Start has %% different values. staring if test_server started with %% -s flag. - ?line case lists:member(Start, [started, starting]) of - true -> - ok; - _ -> - ?t:fail(get_status) - end. + case lists:member(Start, [started, starting]) of + true -> + ok; + _ -> + ct:fail(get_status) + end. %% ------------------------------------------------ %% %% ------------------------------------------------ -script_id(doc) -> []; -script_id(suite) -> []; script_id(Config) when is_list(Config) -> - ?line Dog = ?t:timetrap(?t:seconds(10)), - - ?line {Name, Vsn} = init:script_id(), - ?line if - is_list(Name), is_list(Vsn) -> - ok; - true -> - ?t:fail(not_standard_script) - end, - ?line ?t:timetrap_cancel(Dog), + {Name, Vsn} = init:script_id(), + if + is_list(Name), is_list(Vsn) -> + ok; + true -> + ct:fail(not_standard_script) + end, ok. %% ------------------------------------------------ %% Start the slave system with -boot flag. %% ------------------------------------------------ -boot1(doc) -> []; -boot1(suite) -> {req, [distribution, {local_slave_nodes, 1}, {time, 35}]}; boot1(Config) when is_list(Config) -> - ?line Dog = ?t:timetrap(?t:seconds(80)), Args = args() ++ " -boot start_sasl", - ?line {ok, Node} = start_node(init_test, Args), - ?line stop_node(Node), + {ok, Node} = start_node(init_test, Args), + stop_node(Node), %% Try to start with non existing boot file. Args1 = args() ++ " -boot dummy_script", - ?line {error, timeout} = start_node(init_test, Args1), + {error, timeout} = start_node(init_test, Args1), - ?line ?t:timetrap_cancel(Dog), ok. -boot2(doc) -> []; -boot2(suite) -> {req, [distribution, {local_slave_nodes, 1}, {time, 35}]}; boot2(Config) when is_list(Config) -> - Dog = ?t:timetrap(?t:seconds(80)), - %% Absolute boot file name Boot = filename:join([code:root_dir(), "bin", "start_sasl"]), @@ -603,9 +628,9 @@ boot2(Config) when is_list(Config) -> %% Absolute boot file name for Windows -- all slashes are %% converted to backslashes. Win_boot = lists:map(fun - ($/) -> $\\; - (C) -> C - end, Boot), + ($/) -> $\\; + (C) -> C + end, Boot), Args2 = args() ++ " -boot \"" ++ Win_boot ++ "\"", {ok, Node2} = start_node(init_test, Args2), stop_node(Node2); @@ -613,16 +638,15 @@ boot2(Config) when is_list(Config) -> ok end, - ?t:timetrap_cancel(Dog), ok. %% Misc. functions start_node(Name, Param) -> - ?t:start_node(Name, slave, [{args, Param}]). + test_server:start_node(Name, slave, [{args, Param}]). stop_node(Node) -> - ?t:stop_node(Node). + test_server:stop_node(Node). from(H, [H | T]) -> T; from(H, [_ | T]) -> from(H, T); @@ -638,18 +662,18 @@ long_args(A) -> [A])). create_script(Config) -> - ?line PrivDir = ?config(priv_dir,Config), - ?line Name = PrivDir ++ "boot_var_test", - ?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\", \"P2A\"}, \n" - " {erts, \"4.4\"}, \n" - " [{kernel, \"~s\"}, {stdlib, \"~s\"}]}.\n", - [KernelVer,StdlibVer]), - ?line file:close(Fd), + PrivDir = proplists:get_value(priv_dir,Config), + Name = PrivDir ++ "boot_var_test", + Apps = application_controller:which_applications(), + {value,{_,_,KernelVer}} = lists:keysearch(kernel,1,Apps), + {value,{_,_,StdlibVer}} = lists:keysearch(stdlib,1,Apps), + {ok,Fd} = file:open(Name ++ ".rel", [write]), + io:format(Fd, + "{release, {\"Test release 3\", \"P2A\"}, \n" + " {erts, \"4.4\"}, \n" + " [{kernel, \"~s\"}, {stdlib, \"~s\"}]}.\n", + [KernelVer,StdlibVer]), + file:close(Fd), {filename:dirname(Name), filename:basename(Name), - KernelVer, StdlibVer}. + KernelVer, StdlibVer}. diff --git a/lib/kernel/test/interactive_shell_SUITE.erl b/lib/kernel/test/interactive_shell_SUITE.erl index 7f6024f642..298a364a91 100644 --- a/lib/kernel/test/interactive_shell_SUITE.erl +++ b/lib/kernel/test/interactive_shell_SUITE.erl @@ -1,23 +1,24 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2013. All Rights Reserved. +%% Copyright Ericsson AB 2007-2016. 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% -module(interactive_shell_SUITE). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, get_columns_and_rows/1, exit_initial/1, job_control_local/1, @@ -29,15 +30,14 @@ -export([toerl_server/3]). init_per_testcase(_Func, Config) -> - Dog = test_server:timetrap(test_server:minutes(3)), - [{watchdog,Dog}|Config]. - -end_per_testcase(_Func, Config) -> - Dog = ?config(watchdog, Config), - test_server:timetrap_cancel(Dog). + Config. +end_per_testcase(_Func, _Config) -> + ok. -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap,{minutes,3}}]. all() -> [get_columns_and_rows, exit_initial, job_control_local, @@ -48,18 +48,13 @@ groups() -> []. init_per_suite(Config) -> - Term = case os:getenv("TERM") of - List when is_list(List) -> - List; - _ -> - "dumb" - end, + Term = os:getenv("TERM", "dumb"), os:putenv("TERM","vt100"), DefShell = get_default_shell(), [{default_shell,DefShell},{term,Term}|Config]. end_per_suite(Config) -> - Term = ?config(term,Config), + Term = proplists:get_value(term,Config), os:putenv("TERM",Term), ok. @@ -70,68 +65,66 @@ end_per_group(_GroupName, Config) -> Config. -%-define(DEBUG,1). +%%-define(DEBUG,1). -ifdef(DEBUG). -define(dbg(Data),erlang:display(Data)). -else. -define(dbg(Data),noop). -endif. -get_columns_and_rows(suite) -> []; -get_columns_and_rows(doc) -> ["Test that the shell can access columns and rows"]; +%% Test that the shell can access columns and rows. get_columns_and_rows(Config) when is_list(Config) -> case proplists:get_value(default_shell,Config) of old -> %% Old shell tests ?dbg(old_shell), - ?line rtnode([{putline,""}, - {putline, "2."}, - {getline, "2"}, - {putline,"io:columns()."}, - {getline_re,".*{error,enotsup}"}, - {putline,"io:rows()."}, - {getline_re,".*{error,enotsup}"} - - ],[]), - ?line rtnode([{putline,""}, - {putline, "2."}, - {getline, "2"}, - {putline,"io:columns()."}, - {getline_re,".*{ok,90}"}, - {putline,"io:rows()."}, - {getline_re,".*{ok,40}"}], - [], - "stty rows 40; stty columns 90; "); + rtnode([{putline,""}, + {putline, "2."}, + {getline, "2"}, + {putline,"io:columns()."}, + {getline_re,".*{error,enotsup}"}, + {putline,"io:rows()."}, + {getline_re,".*{error,enotsup}"} + + ],[]), + rtnode([{putline,""}, + {putline, "2."}, + {getline, "2"}, + {putline,"io:columns()."}, + {getline_re,".*{ok,90}"}, + {putline,"io:rows()."}, + {getline_re,".*{ok,40}"}], + [], + "stty rows 40; stty columns 90; "); new -> - % New shell tests + %% New shell tests ?dbg(new_shell), - ?line rtnode([{putline,""}, - {putline, "2."}, - {getline, "2"}, - {putline,"io:columns()."}, - %% Behaviour change in R12B-5, returns 80 - %% {getline,"{error,enotsup}"}, - {getline,"{ok,80}"}, - {putline,"io:rows()."}, - %% Behaviour change in R12B-5, returns 24 - %% {getline,"{error,enotsup}"} - {getline,"{ok,24}"} - ],[]), - ?line rtnode([{putline,""}, - {putline, "2."}, - {getline, "2"}, - {putline,"io:columns()."}, - {getline,"{ok,90}"}, - {putline,"io:rows()."}, - {getline,"{ok,40}"}], - [], - "stty rows 40; stty columns 90; ") + rtnode([{putline,""}, + {putline, "2."}, + {getline, "2"}, + {putline,"io:columns()."}, + %% Behaviour change in R12B-5, returns 80 + %% {getline,"{error,enotsup}"}, + {getline,"{ok,80}"}, + {putline,"io:rows()."}, + %% Behaviour change in R12B-5, returns 24 + %% {getline,"{error,enotsup}"} + {getline,"{ok,24}"} + ],[]), + rtnode([{putline,""}, + {putline, "2."}, + {getline, "2"}, + {putline,"io:columns()."}, + {getline,"{ok,90}"}, + {putline,"io:rows()."}, + {getline,"{ok,40}"}], + [], + "stty rows 40; stty columns 90; ") end. - - -exit_initial(suite) -> []; -exit_initial(doc) -> ["Tests that exit of initial shell restarts shell"]; + + +%% Tests that exit of initial shell restarts shell. exit_initial(Config) when is_list(Config) -> case proplists:get_value(default_shell,Config) of old -> @@ -156,9 +149,7 @@ exit_initial(Config) when is_list(Config) -> {getline_re,"35"}],[]) end. -job_control_local(suite) -> []; -job_control_local(doc) -> [ "Tests that local shell can be " - "started by means of job control" ]; +%% Tests that local shell can be started by means of job control. job_control_local(Config) when is_list(Config) -> case proplists:get_value(default_shell,Config) of old -> @@ -166,133 +157,130 @@ job_control_local(Config) when is_list(Config) -> {skip,"No new shell found"}; new -> %% New shell tests - ?line rtnode([{putline,""}, - {putline, "2."}, - {getline, "2"}, - {putline,[7]}, - {sleep,timeout(short)}, - {putline,""}, - {getline," -->"}, - {putline,"s"}, - {putline,"c"}, - {putline_raw,""}, - {getline,"Eshell"}, - {putline_raw,""}, - {getline,"1>"}, - {putline,"35."}, - {getline,"35"}],[]) + rtnode([{putline,""}, + {putline, "2."}, + {getline, "2"}, + {putline,[7]}, + {sleep,timeout(short)}, + {putline,""}, + {getline," -->"}, + {putline,"s"}, + {putline,"c"}, + {putline_raw,""}, + {getline,"Eshell"}, + {putline_raw,""}, + {getline,"1>"}, + {putline,"35."}, + {getline,"35"}],[]) end. -job_control_remote(suite) -> []; job_control_remote(doc) -> [ "Tests that remote shell can be " "started by means of job control" ]; job_control_remote(Config) when is_list(Config) -> case {node(),proplists:get_value(default_shell,Config)} of {nonode@nohost,_} -> - ?line exit(not_distributed); + exit(not_distributed); {_,old} -> {skip,"No new shell found"}; _ -> - ?line RNode = create_nodename(), - ?line MyNode = atom2list(node()), - ?line Pid = spawn_link(fun() -> - receive die -> - ok - end - end), - ?line PidStr = pid_to_list(Pid), - ?line register(kalaskula,Pid), - ?line CookieString = lists:flatten( - io_lib:format("~w", - [erlang:get_cookie()])), - ?line Res = rtnode([{putline,""}, - {putline, "erlang:get_cookie()."}, - {getline, CookieString}, - {putline,[7]}, - {sleep,timeout(short)}, - {putline,""}, - {getline," -->"}, - {putline,"r '"++MyNode++"'"}, - {putline,"c"}, - {putline_raw,""}, - {getline,"Eshell"}, - {sleep,timeout(short)}, - {putline_raw,""}, - {getline,"("++MyNode++")1>"}, - {putline,"whereis(kalaskula)."}, - {getline,PidStr}, - {sleep,timeout(short)}, % Race, known bug. - {putline_raw,"exit()."}, - {getline,"***"}, - {putline,[7]}, - {putline,""}, - {getline," -->"}, - {putline,"c 1"}, - {putline,""}, - {sleep,timeout(short)}, - {putline_raw,""}, - {getline,"("++RNode++")"}],RNode), - ?line Pid ! die, - ?line Res + RNode = create_nodename(), + MyNode = atom2list(node()), + Pid = spawn_link(fun() -> + receive die -> + ok + end + end), + PidStr = pid_to_list(Pid), + register(kalaskula,Pid), + CookieString = lists:flatten( + io_lib:format("~w", + [erlang:get_cookie()])), + Res = rtnode([{putline,""}, + {putline, "erlang:get_cookie()."}, + {getline, CookieString}, + {putline,[7]}, + {sleep,timeout(short)}, + {putline,""}, + {getline," -->"}, + {putline,"r '"++MyNode++"'"}, + {putline,"c"}, + {putline_raw,""}, + {getline,"Eshell"}, + {sleep,timeout(short)}, + {putline_raw,""}, + {getline,"("++MyNode++")1>"}, + {putline,"whereis(kalaskula)."}, + {getline,PidStr}, + {sleep,timeout(short)}, % Race, known bug. + {putline_raw,"exit()."}, + {getline,"***"}, + {putline,[7]}, + {putline,""}, + {getline," -->"}, + {putline,"c 1"}, + {putline,""}, + {sleep,timeout(short)}, + {putline_raw,""}, + {getline,"("++RNode++")"}],RNode), + Pid ! die, + Res end. -job_control_remote_noshell(suite) -> []; -job_control_remote_noshell(doc) -> - [ "Tests that remote shell can be " - "started by means of job control to -noshell node" ]; + +%% Tests that remote shell can be +%% started by means of job control to -noshell node. job_control_remote_noshell(Config) when is_list(Config) -> case {node(),proplists:get_value(default_shell,Config)} of {nonode@nohost,_} -> - ?line exit(not_distributed); + exit(not_distributed); {_,old} -> {skip,"No new shell found"}; _ -> - ?line RNode = create_nodename(), - ?line NSNode = start_noshell_node(interactive_shell_noshell), - ?line Pid = spawn_link(NSNode, fun() -> - receive die -> - ok - end - end), - ?line PidStr = rpc:call(NSNode,erlang,pid_to_list,[Pid]), - ?line true = rpc:call(NSNode,erlang,register,[kalaskula,Pid]), - ?line NSNodeStr = atom2list(NSNode), - ?line CookieString = lists:flatten( - io_lib:format("~w", - [erlang:get_cookie()])), - ?line Res = rtnode([{putline,""}, - {putline, "erlang:get_cookie()."}, - {getline, CookieString}, - {putline,[7]}, - {sleep,timeout(short)}, - {putline,""}, - {getline," -->"}, - {putline,"r '"++NSNodeStr++"'"}, - {putline,"c"}, - {putline_raw,""}, - {getline,"Eshell"}, - {sleep,timeout(short)}, - {putline_raw,""}, - {getline,"("++NSNodeStr++")1>"}, - {putline,"whereis(kalaskula)."}, - {getline,PidStr}, - {sleep,timeout(short)}, % Race, known bug. - {putline_raw,"exit()."}, - {getline,"***"}, - {putline,[7]}, - {putline,""}, - {getline," -->"}, - {putline,"c 1"}, - {putline,""}, - {sleep,timeout(short)}, - {putline_raw,""}, - {getline,"("++RNode++")"}],RNode), - ?line Pid ! die, - ?line stop_noshell_node(NSNode), - ?line Res + RNode = create_nodename(), + NSNode = start_noshell_node(interactive_shell_noshell), + Pid = spawn_link(NSNode, fun() -> + receive die -> + ok + end + end), + PidStr = rpc:call(NSNode,erlang,pid_to_list,[Pid]), + true = rpc:call(NSNode,erlang,register,[kalaskula,Pid]), + NSNodeStr = atom2list(NSNode), + CookieString = lists:flatten( + io_lib:format("~w", + [erlang:get_cookie()])), + Res = rtnode([{putline,""}, + {putline, "erlang:get_cookie()."}, + {getline, CookieString}, + {putline,[7]}, + {sleep,timeout(short)}, + {putline,""}, + {getline," -->"}, + {putline,"r '"++NSNodeStr++"'"}, + {putline,"c"}, + {putline_raw,""}, + {getline,"Eshell"}, + {sleep,timeout(short)}, + {putline_raw,""}, + {getline,"("++NSNodeStr++")1>"}, + {putline,"whereis(kalaskula)."}, + {getline,PidStr}, + {sleep,timeout(short)}, % Race, known bug. + {putline_raw,"exit()."}, + {getline,"***"}, + {putline,[7]}, + {putline,""}, + {getline," -->"}, + {putline,"c 1"}, + {putline,""}, + {sleep,timeout(short)}, + {putline_raw,""}, + {getline,"("++RNode++")"}],RNode), + Pid ! die, + stop_noshell_node(NSNode), + Res end. -ctrl_keys(suite) -> []; -ctrl_keys(doc) -> ["Tests various control keys"]; +%% Tests various control keys. ctrl_keys(_Conf) when is_list(_Conf) -> Cu=[$\^u], Cw=[$\^w], @@ -312,7 +300,7 @@ ctrl_keys(_Conf) when is_list(_Conf) -> {getline,"\"hello world\""}, {putline,"\"hello world\""++Cu++Cy++"."}, {getline,"\"hello world\""}] - ++wordLeft()++wordRight(),[]). + ++wordLeft()++wordRight(),[]). wordLeft() -> @@ -341,46 +329,46 @@ wordRight(Chars) -> rtnode(C,N) -> rtnode(C,N,[]). rtnode(Commands,Nodename,ErlPrefix) -> - ?line case get_progs() of - {error,_Reason} -> - ?line {skip,"No runerl present"}; - {RunErl,ToErl,Erl} -> - ?line case create_tempdir() of - {error, Reason2} -> - ?line {skip, Reason2}; - Tempdir -> - ?line SPid = - start_runerl_node(RunErl,ErlPrefix++"\\\""++Erl++"\\\"", - Tempdir,Nodename), - ?line CPid = start_toerl_server(ToErl,Tempdir), - ?line erase(getline_skipped), - ?line Res = - (catch get_and_put(CPid, Commands,1)), - ?line case stop_runerl_node(CPid) of - {error,_} -> - ?line CPid2 = - start_toerl_server - (ToErl,Tempdir), - ?line erase(getline_skipped), - ?line ok = get_and_put - (CPid2, - [{putline,[7]}, - {sleep, - timeout(short)}, - {putline,""}, - {getline," -->"}, - {putline,"s"}, - {putline,"c"}, - {putline,""}],1), - ?line stop_runerl_node(CPid2); - _ -> - ?line ok - end, - ?line wait_for_runerl_server(SPid), - ?line ok = rm_rf(Tempdir), - ?line ok = Res - end - end. + case get_progs() of + {error,_Reason} -> + {skip,"No runerl present"}; + {RunErl,ToErl,Erl} -> + case create_tempdir() of + {error, Reason2} -> + {skip, Reason2}; + Tempdir -> + SPid = + start_runerl_node(RunErl,ErlPrefix++"\\\""++Erl++"\\\"", + Tempdir,Nodename), + CPid = start_toerl_server(ToErl,Tempdir), + erase(getline_skipped), + Res = + (catch get_and_put(CPid, Commands,1)), + case stop_runerl_node(CPid) of + {error,_} -> + CPid2 = + start_toerl_server + (ToErl,Tempdir), + erase(getline_skipped), + ok = get_and_put + (CPid2, + [{putline,[7]}, + {sleep, + timeout(short)}, + {putline,""}, + {getline," -->"}, + {putline,"s"}, + {putline,"c"}, + {putline,""}],1), + stop_runerl_node(CPid2); + _ -> + ok + end, + wait_for_runerl_server(SPid), + ok = rm_rf(Tempdir), + ok = Res + end + end. timeout(long) -> 2 * timeout(normal); @@ -393,7 +381,7 @@ timeout(normal) -> start_noshell_node(Name) -> PADir = filename:dirname(code:which(?MODULE)), {ok, Node} = test_server:start_node(Name,slave,[{args," -noshell -pa "++ - PADir++" "}]), + PADir++" "}]), Node. stop_noshell_node(Node) -> test_server:stop_node(Node). @@ -401,20 +389,20 @@ stop_noshell_node(Node) -> rm_rf(Dir) -> try - {ok,List} = file:list_dir(Dir), - Files = [filename:join([Dir,X]) || X <- List], - [case file:list_dir(Y) of - {error, enotdir} -> - ok = file:delete(Y); - _ -> - ok = rm_rf(Y) - end || Y <- Files], - ok = file:del_dir(Dir), - ok + {ok,List} = file:list_dir(Dir), + Files = [filename:join([Dir,X]) || X <- List], + [case file:list_dir(Y) of + {error, enotdir} -> + ok = file:delete(Y); + _ -> + ok = rm_rf(Y) + end || Y <- Files], + ok = file:del_dir(Dir), + ok catch _:Exception -> {error, {Exception,Dir}} end. - + get_and_put(_CPid,[],_) -> ok; @@ -483,7 +471,7 @@ get_and_put(CPid, [{putline_raw, Line}|T],N) -> Timeout = timeout(normal), receive {send_line, ok} -> - get_and_put(CPid, T,N+1) + get_and_put(CPid, T,N+1) after Timeout -> error_logger:error_msg("~p: putline_raw timeout (~p) sending " "\"~s\" (command number ~p)~n", @@ -497,7 +485,7 @@ get_and_put(CPid, [{putline, Line}|T],N) -> Timeout = timeout(normal), receive {send_line, ok} -> - get_and_put(CPid, [{getline, []}|T],N) + get_and_put(CPid, [{getline, []}|T],N) after Timeout -> error_logger:error_msg("~p: putline timeout (~p) sending " "\"~s\" (command number ~p)~n[~p]~n", @@ -514,8 +502,8 @@ wait_for_runerl_server(SPid) -> after Timeout -> {error, timeout} end. - - + + stop_runerl_node(CPid) -> Ref = erlang:monitor(process, CPid), @@ -566,11 +554,11 @@ create_tempdir(Dir,X) when X > $Z, X < $a -> create_tempdir(Dir,$a); create_tempdir(Dir,X) when X > $z -> Estr = lists:flatten( - io_lib:format("Unable to create ~s, reason eexist", - [Dir++[$z]])), + io_lib:format("Unable to create ~s, reason eexist", + [Dir++[$z]])), {error, Estr}; create_tempdir(Dir0, Ch) -> - % Expect fairly standard unix. + %% Expect fairly standard unix. Dir = Dir0++[Ch], case file:make_dir(Dir) of {error, eexist} -> @@ -608,13 +596,13 @@ start_runerl_node(RunErl,Erl,Tempdir,Nodename) -> []; _ -> " -sname "++(if is_atom(Nodename) -> atom_to_list(Nodename); - true -> Nodename - end)++ + true -> Nodename + end)++ " -setcookie "++atom_to_list(erlang:get_cookie()) end, spawn(fun() -> os:cmd("\""++RunErl++"\" "++Tempdir++"/ "++Tempdir++" \""++ - Erl++XArg++"\"") + Erl++XArg++"\"") end). start_toerl_server(ToErl,Tempdir) -> @@ -672,7 +660,7 @@ toerl_loop(Port,Acc) -> _ -> toerl_loop(Port,[{Tag0,Data}|Acc]) end; - {Pid,{get_line,Timeout}} -> + {Pid,{get_line,Timeout}} -> case Acc of [] -> case get_data_within(Port,Timeout,[]) of @@ -721,11 +709,10 @@ toerl_loop(Port,Acc) -> Other -> {error, {unexpected, Other}} end. - + millistamp() -> - {Mega, Secs, Micros} = erlang:now(), - (Micros div 1000) + Secs * 1000 + Mega * 1000000000. - + erlang:monotonic_time(millisecond). + get_data_within(Port, X, Acc) when X =< 0 -> ?dbg({get_data_within, X, Acc, ?LINE}), receive @@ -756,7 +743,7 @@ get_data_within(Port, Timeout, Acc) -> after Timeout -> timeout end. - + get_default_shell() -> try rtnode([{putline,""}, diff --git a/lib/kernel/test/kernel_SUITE.erl b/lib/kernel/test/kernel_SUITE.erl index 613efeeb2f..3e5ed855b5 100644 --- a/lib/kernel/test/kernel_SUITE.erl +++ b/lib/kernel/test/kernel_SUITE.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2014. All Rights Reserved. +%% Copyright Ericsson AB 1997-2018. 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/. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at %% -%% 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. +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% @@ -20,24 +21,23 @@ %%% Kernel application test suite. %%%----------------------------------------------------------------- -module(kernel_SUITE). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -% Test server specific exports +%% Test server specific exports -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, 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, appup_test/1]). +%% Test cases must be exported. +-export([app_test/1, appup_test/1, refc/1]). -%% -%% all/1 -%% -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap,{minutes,2}}]. all() -> - [app_test, appup_test]. + [app_test, appup_test, refc]. groups() -> []. @@ -60,15 +60,12 @@ init_per_testcase(_Case, Config) -> end_per_testcase(_Case, _Config) -> ok. -% -% Test cases starts here. -% -app_test(doc) -> - ["Tests the applications consistency."]; -app_test(suite) -> - []; +%% +%% Test cases starts here. +%% +%% Tests the applications consistency. app_test(Config) when is_list(Config) -> - ?line ok=?t:app_test(kernel), + ok=test_server:app_test(kernel), ok. @@ -166,3 +163,68 @@ check_appup([Vsn|Vsns],Instrs,Expected) -> end; check_appup([],_,_) -> ok. + +%%% Check that refc module handles the counters as expected +refc(_Config) -> + Enable = fun(Enable) -> erlang:system_flag(scheduler_wall_time, Enable) end, + IsOn = fun() -> undefined /= erlang:statistics(scheduler_wall_time) end, + Tester = self(), + Loop = fun Loop() -> + receive + die -> normal; + {apply, Bool} -> + Res = Enable(Bool), + Tester ! {self(), Res}, + Loop() + end + end, + + %% Counter should be 0 + false = Enable(false), + + false = Enable(true), + true = Enable(true), + true = Enable(false), + true = Enable(false), + + %% Counter should be 0 + false = IsOn(), + + P1 = spawn_link(Loop), + P1 ! {apply, true}, + receive {P1, R1} -> false = R1 end, + + %% P1 has turned it on counter should be one + true = IsOn(), + true = Enable(true), + true = Enable(false), + true = IsOn(), + + P1 ! {apply, false}, + receive {P1, R2} -> true = R2 end, + false = IsOn(), + + P1 ! {apply, true}, + receive {P1, R3} -> false = R3 end, + true = IsOn(), + true = Enable(false), + + + P1 ! die, + timer:sleep(100), + false = IsOn(), + false = Enable(false), + + P2 = spawn_link(Loop), + P2 ! {apply, true}, + receive {P2, R4} -> false = R4 end, + true = IsOn(), + P2 ! {apply, true}, + receive {P2, R5} -> true = R5 end, + true = IsOn(), + + P2 ! die, + timer:sleep(100), + false = IsOn(), + + ok. diff --git a/lib/kernel/test/kernel_bench.spec b/lib/kernel/test/kernel_bench.spec new file mode 100644 index 0000000000..4de133f21b --- /dev/null +++ b/lib/kernel/test/kernel_bench.spec @@ -0,0 +1,2 @@ +{groups,"../kernel_test",zlib_SUITE,[bench]}. +{groups,"../kernel_test",file_SUITE,[bench]}. diff --git a/lib/kernel/test/kernel_config_SUITE.erl b/lib/kernel/test/kernel_config_SUITE.erl index 93bdb8657c..9207025a2c 100644 --- a/lib/kernel/test/kernel_config_SUITE.erl +++ b/lib/kernel/test/kernel_config_SUITE.erl @@ -1,30 +1,33 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2011. All Rights Reserved. +%% Copyright Ericsson AB 1996-2018. 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/. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at %% -%% 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. +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% -module(kernel_config_SUITE). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2, sync/1]). -export([init_per_suite/1, end_per_suite/1]). -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap,{minutes,2}}]. all() -> [sync]. @@ -39,13 +42,9 @@ end_per_group(_GroupName, Config) -> Config. -init_per_suite(doc) -> []; -init_per_suite(suite) -> []; init_per_suite(Config) when is_list(Config) -> Config. -end_per_suite(doc) -> []; -end_per_suite(suite) -> []; end_per_suite(Config) when is_list(Config) -> stop_node(init_test), Config. @@ -53,7 +52,7 @@ end_per_suite(Config) when is_list(Config) -> config(Fd) -> M = from($@, atom_to_list(node())), io:format(Fd, "[{kernel, [{sync_nodes_optional, ['cp1@~s','cp2@~s']}," - "{sync_nodes_timeout, 15000}]}].~n", + "{sync_nodes_timeout, 15000}]}].~n", [M, M]). from(H, [H | T]) -> T; @@ -66,12 +65,9 @@ from(_, []) -> []. %% Should be started in a CC view with: %% erl -sname XXX where XX not in [cp1, cp2] %%----------------------------------------------------------------- -sync(doc) -> []; -sync(suite) -> []; sync(Conf) when is_list(Conf) -> - ?line Dog = ?t:timetrap(?t:seconds(120)), - % Write a config file - Dir = ?config(priv_dir,Conf), + %% Write a config file + Dir = proplists:get_value(priv_dir,Conf), {ok, Fd} = file:open(Dir ++ "sys.config", [write]), config(Fd), file:close(Fd), @@ -80,34 +76,33 @@ sync(Conf) when is_list(Conf) -> %% Reset wall_clock {T1,_} = erlang:statistics(wall_clock), io:format("~p~n", [{t1, T1}]), - ?line Command = lists:concat([lib:progname(), - " -detached -sname cp1 ", - "-config ", Config, - " -env ERL_CRASH_DUMP erl_crash_dump.cp1"]), + Command = lists:append([ct:get_progname(), + " -detached -sname cp1 ", + "-config ", Config, + " -env ERL_CRASH_DUMP erl_crash_dump.cp1"]), io:format("Command: ~s", [Command]), - ?line open_port({spawn, Command}, [stream]), + open_port({spawn, Command}, [stream]), io:format("started~n"), - ?line ?t:sleep(12000), + ct:sleep(12000), io:format("waited12~n"), - ?line Host = from($@, atom_to_list(node())), - ?line Cp1 = list_to_atom("cp1@"++Host), - ?line wait_for_node(Cp1), + Host = from($@, atom_to_list(node())), + Cp1 = list_to_atom("cp1@"++Host), + wait_for_node(Cp1), io:format("waitednode~n"), %% Check time since last call - ?line {TT, T} = erlang:statistics(wall_clock), + {TT, T} = erlang:statistics(wall_clock), io:format("~p~n", [{t2, {TT, T}}]), - ?line stop_node(cp1), + stop_node(cp1), if - TT-T1 < 15000 -> ?line ?t:fail({too_short_time, TT-T1}); + TT-T1 < 15000 -> ct:fail({too_short_time, TT-T1}); true -> ok end, - ?line ?t:timetrap_cancel(Dog), ok. wait_for_node(Node) -> case rpc:call(Node, init, get_status, []) of {started,_} -> ok; - {badrpc, R} -> ?line ?t:fail({rpc_failed, R}); + {badrpc, R} -> ct:fail({rpc_failed, R}); _Other -> wait_for_node(Node) end. diff --git a/lib/kernel/test/logger.cover b/lib/kernel/test/logger.cover new file mode 100644 index 0000000000..960bc0abff --- /dev/null +++ b/lib/kernel/test/logger.cover @@ -0,0 +1,14 @@ +%% -*- erlang -*- +{incl_mods,[error_logger, + logger, + logger_backend, + logger_config, + logger_disk_log_h, + logger_h_common, + logger_filters, + logger_formatter, + logger_server, + logger_simple_h, + logger_std_h, + logger_sup]}. + diff --git a/lib/kernel/test/logger.spec b/lib/kernel/test/logger.spec new file mode 100644 index 0000000000..1ab90b3e93 --- /dev/null +++ b/lib/kernel/test/logger.spec @@ -0,0 +1,11 @@ +%% -*-erlang-*- +{suites,"../kernel_test", [error_logger_SUITE, + error_logger_warn_SUITE, + logger_SUITE, + logger_disk_log_h_SUITE, + logger_env_var_SUITE, + logger_filters_SUITE, + logger_formatter_SUITE, + logger_legacy_SUITE, + logger_simple_h_SUITE, + logger_std_h_SUITE]}. diff --git a/lib/kernel/test/logger_SUITE.erl b/lib/kernel/test/logger_SUITE.erl new file mode 100644 index 0000000000..b7ccba8e70 --- /dev/null +++ b/lib/kernel/test/logger_SUITE.erl @@ -0,0 +1,1318 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2018. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% +-module(logger_SUITE). + +-compile(export_all). + +-include_lib("common_test/include/ct.hrl"). +-include_lib("kernel/include/logger.hrl"). +-include_lib("kernel/src/logger_internal.hrl"). + +-define(str,"Log from "++atom_to_list(?FUNCTION_NAME)++ + ":"++integer_to_list(?LINE)). +-define(map_rep,#{function=>?FUNCTION_NAME, line=>?LINE}). +-define(keyval_rep,[{function,?FUNCTION_NAME}, {line,?LINE}]). + +-define(MY_LOC(N),#{mfa=>{?MODULE,?FUNCTION_NAME,?FUNCTION_ARITY}, + file=>?FILE, line=>?LINE-N}). + +-define(TRY(X), my_try(fun() -> X end)). + + +suite() -> + [{timetrap,{seconds,30}}, + {ct_hooks,[logger_test_lib]}]. + +init_per_suite(Config) -> + case logger:get_handler_config(?STANDARD_HANDLER) of + {ok,StdH} -> + ok = logger:remove_handler(?STANDARD_HANDLER), + [{default_handler,StdH}|Config]; + _ -> + Config + end. + +end_per_suite(Config) -> + case ?config(default_handler,Config) of + #{module:=HMod} = HConfig -> + ok = logger:add_handler(?STANDARD_HANDLER,HMod,HConfig); + _ -> + ok + end. + +init_per_group(_Group, Config) -> + Config. + +end_per_group(_Group, _Config) -> + ok. + +init_per_testcase(_TestCase, Config) -> + PC = logger:get_primary_config(), + [{logger_config,PC}|Config]. + +end_per_testcase(Case, Config) -> + try apply(?MODULE,Case,[cleanup,Config]) + catch error:undef -> ok + end, + ok. + +groups() -> + []. + +all() -> + [start_stop, + add_remove_handler, + multiple_handlers, + add_remove_filter, + change_config, + set_formatter, + log_no_levels, + log_all_levels_api, + macros, + set_level, + set_module_level, + set_application_level, + cache_module_level, + format_report, + filter_failed, + handler_failed, + config_sanity_check, + log_failed, + emulator, + via_logger_process, + other_node, + compare_levels, + process_metadata, + app_config, + kernel_config]. + +start_stop(_Config) -> + S = whereis(logger), + true = is_pid(S), + ok. + +add_remove_handler(_Config) -> + register(callback_receiver,self()), + Hs0 = logger:get_handler_config(), + {error,{not_found,h1}} = logger:get_handler_config(h1), + ok = logger:add_handler(h1,?MODULE,#{}), + [add] = test_server:messages_get(), + Hs = logger:get_handler_config(), + Hs0 = lists:filter(fun(#{id:=h1}) -> false; (_) -> true end, Hs), + {ok,#{module:=?MODULE,level:=all,filters:=[],filter_default:=log}} = %defaults + logger:get_handler_config(h1), + ok = logger:set_handler_config(h1,filter_default,stop), + [changing_config] = test_server:messages_get(), + ?LOG_NOTICE("hello",[]), + ok = check_no_log(), + ok = logger:set_handler_config(h1,filter_default,log), + [changing_config] = test_server:messages_get(), + {ok,#{filter_default:=log}} = logger:get_handler_config(h1), + ?LOG_NOTICE("hello",[]), + ok = check_logged(notice,"hello",[],?MY_LOC(1)), + ok = logger:remove_handler(h1), + [remove] = test_server:messages_get(), + Hs0 = logger:get_handler_config(), + {error,{not_found,h1}} = logger:get_handler_config(h1), + {error,{not_found,h1}} = logger:remove_handler(h1), + logger:notice("hello",[]), + ok = check_no_log(), + ok. + +add_remove_handler(cleanup,_Config) -> + logger:remove_handler(h1), + ok. + +multiple_handlers(_Config) -> + ok = logger:add_handler(h1,?MODULE,#{level=>notice,filter_default=>log}), + ok = logger:add_handler(h2,?MODULE,#{level=>error,filter_default=>log}), + ?LOG_ERROR("hello",[]), + ok = check_logged(error,"hello",[],?MY_LOC(1)), + ok = check_logged(error,"hello",[],?MY_LOC(2)), + ?LOG_NOTICE("hello",[]), + ok = check_logged(notice,"hello",[],?MY_LOC(1)), + ok = check_no_log(), + ok. + +multiple_handlers(cleanup,_Config) -> + logger:remove_handler(h1), + logger:remove_handler(h2), + ok. + +add_remove_filter(_Config) -> + ok = logger:add_handler(h1,?MODULE,#{level=>notice,filter_default=>log}), + LF = {fun(Log,_) -> Log#{level=>error} end, []}, + ok = logger:add_primary_filter(lf,LF), + {error,{already_exist,lf}} = logger:add_primary_filter(lf,LF), + {error,{already_exist,lf}} = logger:add_primary_filter(lf,{fun(Log,_) -> + Log + end, []}), + ?LOG_NOTICE("hello",[]), + ok = check_logged(error,"hello",[],?MY_LOC(1)), + ok = check_no_log(), + + ok = logger:add_handler(h2,?MODULE,#{level=>notice,filter_default=>log}), + HF = {fun(#{level:=error}=Log,_) -> + Log#{level=>mylevel}; + (_,_) -> + ignore + end, + []}, + ok = logger:add_handler_filter(h1,hf,HF), + {error,{already_exist,hf}} = logger:add_handler_filter(h1,hf,HF), + {error,{already_exist,hf}} = logger:add_handler_filter(h1,hf,{fun(Log,_) -> + Log + end, []}), + ?LOG_NOTICE("hello",[]), + ok = check_logged(mylevel,"hello",[],?MY_LOC(1)), + ok = check_logged(error,"hello",[],?MY_LOC(2)), + + ok = logger:remove_primary_filter(lf), + {error,{not_found,lf}} = logger:remove_primary_filter(lf), + + ?LOG_NOTICE("hello",[]), + ok = check_logged(notice,"hello",[],?MY_LOC(1)), + ok = check_logged(notice,"hello",[],?MY_LOC(2)), + + ?LOG_ERROR("hello",[]), + ok = check_logged(mylevel,"hello",[],?MY_LOC(1)), + ok = check_logged(error,"hello",[],?MY_LOC(2)), + + ok = logger:remove_handler_filter(h1,hf), + {error,{not_found,hf}} = logger:remove_handler_filter(h1,hf), + ?LOG_NOTICE("hello",[]), + ok = check_logged(notice,"hello",[],?MY_LOC(1)), + ok = check_logged(notice,"hello",[],?MY_LOC(2)), + + ?LOG_ERROR("hello",[]), + ok = check_logged(error,"hello",[],?MY_LOC(1)), + ok = check_logged(error,"hello",[],?MY_LOC(2)), + ok. + +add_remove_filter(cleanup,_Config) -> + logger:remove_primary_filter(lf), + logger:remove_handler(h1), + logger:remove_handler(h2), + ok. + +change_config(_Config) -> + %% Overwrite handler config - check that defaults are added + {error,{not_found,h1}} = logger:set_handler_config(h1,#{}), + ok = logger:add_handler(h1,?MODULE,#{level=>notice,custom=>custom}), + {ok,#{module:=?MODULE,level:=notice,filter_default:=log,custom:=custom}} = + logger:get_handler_config(h1), + register(callback_receiver,self()), + ok = logger:set_handler_config(h1,#{filter_default=>stop}), + [changing_config] = test_server:messages_get(), + {ok,#{module:=?MODULE,level:=all,filter_default:=stop}=C2} = + logger:get_handler_config(h1), + false = maps:is_key(custom,C2), + {error,fail} = logger:set_handler_config(h1,#{conf_call=>fun() -> {error,fail} end}), + {error,{attempting_syncronous_call_to_self,_}} = + logger:set_handler_config( + h1,#{conf_call=>fun() -> logger:set_handler_config(?MODULE,#{}) end}), + ok = + logger:set_handler_config( + h1,#{conf_call=>fun() -> logger:set_module_level(?MODULE,debug) end}), + {ok,C2} = logger:get_handler_config(h1), + + %% Change handler config: Single key + {error,fail} = logger:set_handler_config(h1,conf_call,fun() -> {error,fail} end), + ok = logger:set_handler_config(h1,custom,custom), + [changing_config] = test_server:messages_get(), + {ok,#{custom:=custom}=C3} = logger:get_handler_config(h1), + C2 = maps:remove(custom,C3), + + %% Change handler config: Map + ok = logger:update_handler_config(h1,#{custom=>new_custom}), + [changing_config] = test_server:messages_get(), + {ok,C4} = logger:get_handler_config(h1), + C4 = C3#{custom:=new_custom}, + + %% Change primary config: Single key + PConfig0 = logger:get_primary_config(), + ok = logger:set_primary_config(level,warning), + PConfig1 = logger:get_primary_config(), + PConfig1 = PConfig0#{level:=warning}, + + %% Change primary config: Map + ok = logger:update_primary_config(#{level=>error}), + PConfig2 = logger:get_primary_config(), + PConfig2 = PConfig1#{level:=error}, + + %% Overwrite primary config - check that defaults are added + ok = logger:set_primary_config(#{filter_default=>stop}), + #{level:=notice,filters:=[],filter_default:=stop}=PC1 = + logger:get_primary_config(), + 3 = maps:size(PC1), + %% Check that internal 'handlers' field has not been changed + MS = [{{{?HANDLER_KEY,'$1'},'_','_'},[],['$1']}], + HIds1 = lists:sort(ets:select(?LOGGER_TABLE,MS)), % dirty, internal data + HIds2 = lists:sort(logger:get_handler_ids()), + HIds1 = HIds2, + + %% Cleanup + ok = logger:set_primary_config(PConfig0), + [] = test_server:messages_get(), + + ok. + +change_config(cleanup,Config) -> + logger:remove_handler(h1), + PC = ?config(logger_config,Config), + logger:set_primary_config(PC), + ok. + +set_formatter(_Config) -> + {error,{not_found,h1}}=logger:set_handler_config(h1,formatter,{?MODULE,[]}), + ok = logger:add_handler(h1,?MODULE,#{level=>notice,filter_default=>log}), + ok = logger:set_handler_config(h1,formatter,{?MODULE,[]}), + logger:notice("hello",[]), + receive + {_Log,#{formatter:={?MODULE,[]}}} -> + ok + after 500 -> + ct:fail({timeout,no_log,process_info(self(),messages)}) + end, + ok. + +set_formatter(cleanup,_Config) -> + logger:remove_handler(h1), + ok. + +log_no_levels(_Config) -> + ok = logger:add_handler(h1,?MODULE,#{level=>all,filter_default=>log}), + logger:notice(M1=?map_rep), + ok = check_logged(notice,M1,#{}), + + Levels = [emergency,alert,critical,error,warning,notice,info,debug], + ok = logger:set_primary_config(level,none), + [logger:Level(#{Level=>rep}) || Level <- Levels], + ok = check_no_log(), + + ok = logger:set_primary_config(level,all), + M2 = ?map_rep, + ?LOG_NOTICE(M2), + ok = check_logged(notice,M2,#{}), + + ok = logger:set_module_level(?MODULE,none), + ?LOG_EMERGENCY(?map_rep), + ?LOG_ALERT(?map_rep), + ?LOG_CRITICAL(?map_rep), + ?LOG_ERROR(?map_rep), + ?LOG_WARNING(?map_rep), + ?LOG_NOTICE(?map_rep), + ?LOG_INFO(?map_rep), + ?LOG_DEBUG(?map_rep), + ok = check_no_log(), + + ok = logger:unset_module_level(?MODULE), + logger:notice(M3=?map_rep), + ok = check_logged(notice,M3,#{}), + + ok = logger:set_handler_config(h1,level,none), + [logger:Level(#{Level=>rep}) || Level <- Levels], + ok = check_no_log(), + + ok. +log_no_levels(cleanup,_Config) -> + logger:remove_handler(h1), + logger:set_primary_config(level,notice), + logger:unset_module_level(?MODULE), + ok. + +log_all_levels_api(_Config) -> + ok = logger:set_primary_config(level,all), + ok = logger:add_handler(h1,?MODULE,#{level=>all,filter_default=>log}), + test_api(emergency), + test_api(alert), + test_api(critical), + test_api(error), + test_api(warning), + test_api(notice), + test_api(info), + test_api(debug), + test_log_function(emergency), + ok. + +log_all_levels_api(cleanup,_Config) -> + logger:remove_handler(h1), + logger:set_primary_config(level,notice), + ok. + +macros(_Config) -> + ok = logger:add_handler(h1,?MODULE,#{level=>all,filter_default=>log}), + test_macros(emergency), + test_log_macro(alert), + ok. + +macros(cleanup,_Config) -> + logger:remove_handler(h1), + logger:unset_module_level(?MODULE), + ok. + +set_level(_Config) -> + ok = logger:add_handler(h1,?MODULE,#{level=>all,filter_default=>log}), + logger:debug(?map_rep), + ok = check_no_log(), + logger:notice(M1=?map_rep), + ok = check_logged(notice,M1,#{}), + ok = logger:set_primary_config(level,debug), + logger:debug(M2=?map_rep), + ok = check_logged(debug,M2,#{}), + ok. + +set_level(cleanup,_Config) -> + logger:remove_handler(h1), + logger:set_primary_config(level,notice), + ok. + +set_module_level(_Config) -> + [] = logger:get_module_level([?MODULE,other]), + [] = logger:get_module_level(?MODULE), + [] = logger:get_module_level(), + + ok = logger:add_handler(h1,?MODULE,#{level=>notice,filter_default=>log}), + {error,{invalid_level,bad}} = logger:set_module_level(?MODULE,bad), + {error,{not_a_list_of_modules,{bad}}} = + logger:set_module_level({bad},warning), + {error,{not_a_list_of_modules,[{bad}]}} = + logger:set_module_level([{bad}],warning), + ok = logger:set_module_level(?MODULE,warning), + [{?MODULE,warning}] = logger:get_module_level([?MODULE,other]), + [{?MODULE,warning}] = logger:get_module_level(?MODULE), + [{?MODULE,warning}] = logger:get_module_level(), + logger:notice(?map_rep,?MY_LOC(0)), + ok = check_no_log(), + logger:warning(M1=?map_rep,?MY_LOC(0)), + ok = check_logged(warning,M1,?MY_LOC(1)), + ok = logger:set_module_level(?MODULE,notice), + [{?MODULE,notice}] = logger:get_module_level([?MODULE,other]), + [{?MODULE,notice}] = logger:get_module_level(?MODULE), + [{?MODULE,notice}] = logger:get_module_level(), + logger:notice(M2=?map_rep,?MY_LOC(0)), + ok = check_logged(notice,M2,?MY_LOC(1)), + + {error,{not_a_list_of_modules,{bad}}} = logger:unset_module_level({bad}), + {error,{not_a_list_of_modules,[{bad}]}} = logger:unset_module_level([{bad}]), + ok = logger:unset_module_level(?MODULE), + [] = logger:get_module_level([?MODULE,other]), + [] = logger:get_module_level(?MODULE), + [] = logger:get_module_level(), + + ok = logger:set_module_level([m1,m2,m3],notice), + [{m1,notice},{m2,notice},{m3,notice}] = logger:get_module_level(), + ok = logger:unset_module_level(m2), + [{m1,notice},{m3,notice}] = logger:get_module_level(), + ok = logger:unset_module_level(), + [] = logger:get_module_level(), + + ok. + +set_module_level(cleanup,_Config) -> + logger:remove_handler(h1), + logger:unset_module_level(?MODULE), + ok. + +set_application_level(_Config) -> + + {error,{not_loaded,mnesia}} = logger:set_application_level(mnesia, warning), + {error,{not_loaded,mnesia}} = logger:unset_application_level(mnesia), + + case application:load(mnesia) of + ok -> + {ok, Modules} = application:get_key(mnesia, modules), + [] = logger:get_module_level(Modules), + + {error,{invalid_level,warn}} = + logger:set_application_level(mnesia, warn), + + ok = logger:set_application_level(mnesia, debug), + DebugModules = lists:sort([{M,debug} || M <- Modules]), + DebugModules = lists:sort(logger:get_module_level(Modules)), + + ok = logger:set_application_level(mnesia, warning), + + WarnModules = lists:sort([{M,warning} || M <- Modules]), + WarnModules = lists:sort(logger:get_module_level(Modules)), + + ok = logger:unset_application_level(mnesia), + [] = logger:get_module_level(Modules); + {error,{"no such file or directory","mnesia.app"}} -> + {skip, "Cannot load mnesia, does not exist"} + end. + +set_application_level(cleanup,_Config) -> + _ = logger:unset_application_level(mnesia), + _ = application:unload(mnesia), + ok. + +cache_module_level(_Config) -> + ok = logger:unset_module_level(?MODULE), + [] = ets:lookup(?LOGGER_TABLE,?MODULE), %dirty - add API in logger_config? + ?LOG_NOTICE(?map_rep), + %% Caching is done asynchronously, so wait a bit for the update + timer:sleep(100), + [_] = ets:lookup(?LOGGER_TABLE,?MODULE), %dirty - add API in logger_config? + ok = logger:unset_module_level(?MODULE), + [] = ets:lookup(?LOGGER_TABLE,?MODULE), %dirty - add API in logger_config? + ok. + +cache_module_level(cleanup,_Config) -> + logger:unset_module_level(?MODULE), + ok. + +format_report(_Config) -> + {"~ts",["string"]} = logger:format_report("string"), + {"~tp",[term]} = logger:format_report(term), + {"~tp",[[]]} = logger:format_report([]), + {" ~tp: ~tp",[key,value]} = logger:format_report([{key,value}]), + KeyVals = [{key1,value1},{key2,"value2"},{key3,[]}], + KeyValRes = + {" ~tp: ~tp\n ~tp: ~ts\n ~tp: ~tp", + [key1,value1,key2,"value2",key3,[]]} = + logger:format_report(KeyVals), + KeyValRes = logger:format_report(maps:from_list(KeyVals)), + KeyValRes = logger:format_otp_report(#{label=>{?MODULE,test},report=>KeyVals}), + {" ~tp: ~tp\n ~tp: ~tp", + [label,{?MODULE,test},report,KeyVals]} = + logger:format_report(#{label=>{?MODULE,test},report=>KeyVals}), + + {" ~tp: ~tp\n ~tp",[key1,value1,term]} = + logger:format_report([{key1,value1},term]), + + {" ~tp: ~tp\n ~tp",[key1,value1,[]]} = + logger:format_report([{key1,value1},[]]), + + {"~tp",[[]]} = logger:format_report([[],[],[]]), + + ok. + +filter_failed(_Config) -> + ok = logger:add_handler(h1,?MODULE,#{level=>notice,filter_default=>log}), + + %% Logger filters + {error,{invalid_filter,_}} = + logger:add_primary_filter(lf,{fun(_) -> ok end,args}), + ok = logger:add_primary_filter(lf, + {fun(_,_) -> + erlang:error({badmatch,b}) + end, + args}), + #{filters:=[_]} = logger:get_primary_config(), + ok = logger:notice(M1=?map_rep), + ok = check_logged(notice,M1,#{}), + {error,{not_found,lf}} = logger:remove_primary_filter(lf), + + ok = logger:add_primary_filter(lf,{fun(_,_) -> faulty_return end,args}), + #{filters:=[_]} = logger:get_primary_config(), + ok = logger:notice(M2=?map_rep), + ok = check_logged(notice,M2,#{}), + {error,{not_found,lf}} = logger:remove_primary_filter(lf), + + %% Handler filters + {error,{not_found,h0}} = + logger:add_handler_filter(h0,hf,{fun(_,_) -> ignore end,args}), + {error,{not_found,h0}} = logger:remove_handler_filter(h0,hf), + {error,{invalid_filter,_}} = + logger:add_handler_filter(h1,hf,{fun(_) -> ok end,args}), + ok = logger:add_handler_filter(h1,hf, + {fun(_,_) -> + erlang:error({badmatch,b}) + end, + args}), + {ok,#{filters:=[_]}} = logger:get_handler_config(h1), + ok = logger:notice(M3=?map_rep), + ok = check_logged(notice,M3,#{}), + {error,{not_found,hf}} = logger:remove_handler_filter(h1,hf), + + ok = logger:add_handler_filter(h1,hf,{fun(_,_) -> faulty_return end,args}), + {ok,#{filters:=[_]}} = logger:get_handler_config(h1), + ok = logger:notice(M4=?map_rep), + ok = check_logged(notice,M4,#{}), + {error,{not_found,hf}} = logger:remove_handler_filter(h1,hf), + + ok. + +filter_failed(cleanup,_Config) -> + logger:remove_handler(h1), + ok. + +handler_failed(_Config) -> + register(callback_receiver,self()), + {error,{invalid_id,1}} = logger:add_handler(1,?MODULE,#{}), + {error,{invalid_module,"nomodule"}} = logger:add_handler(h1,"nomodule",#{}), + {error,{invalid_config,bad}} = logger:add_handler(h1,?MODULE,bad), + {error,{invalid_filters,false}} = + logger:add_handler(h1,?MODULE,#{filters=>false}), + {error,{invalid_filter_default,true}} = + logger:add_handler(h1,?MODULE,#{filter_default=>true}), + {error,{invalid_formatter,[]}} = + logger:add_handler(h1,?MODULE,#{formatter=>[]}), + {error,{invalid_handler,_}} = logger:add_handler(h1,nomodule,#{filter_default=>log}), + logger:notice(?map_rep), + check_no_log(), + H1 = logger:get_handler_config(), + false = lists:search(fun(#{id:=h1}) -> true; (_) -> false end,H1), + {error,{not_found,h1}} = logger:remove_handler(h1), + + ok = logger:add_handler(h2,?MODULE, + #{filter_default => log, + log_call => fun() -> + erlang:error({badmatch,b}) + end}), + {error,{already_exist,h2}} = logger:add_handler(h2,othermodule,#{}), + [add] = test_server:messages_get(), + + logger:notice(?map_rep), + [remove] = test_server:messages_get(), + H2 = logger:get_handler_config(), + false = lists:search(fun(#{id:=h2}) -> true; (_) -> false end,H2), + {error,{not_found,h2}} = logger:remove_handler(h2), + + CallAddHandler = fun() -> logger:add_handler(h2,?MODULE,#{}) end, + CrashHandler = fun() -> erlang:error({badmatch,b}) end, + KillHandler = fun() -> exit(self(), die) end, + + {error,{handler_not_added,{attempting_syncronous_call_to_self,_}}} = + logger:add_handler(h1,?MODULE,#{add_call=>CallAddHandler}), + {error,{handler_not_added,{callback_crashed,_}}} = + logger:add_handler(h1,?MODULE,#{add_call=>CrashHandler}), + {error,{handler_not_added,{logger_process_exited,_,die}}} = + logger:add_handler(h1,?MODULE,#{add_call=>KillHandler}), + + check_no_log(), + ok = logger:add_handler(h1,?MODULE,#{}), + {error,{attempting_syncronous_call_to_self,_}} = + logger:set_handler_config(h1,#{conf_call=>CallAddHandler}), + {error,{callback_crashed,_}} = + logger:set_handler_config(h1,#{conf_call=>CrashHandler}), + {error,{logger_process_exited,_,die}} = + logger:set_handler_config(h1,#{conf_call=>KillHandler}), + + {error,{attempting_syncronous_call_to_self,_}} = + logger:set_handler_config(h1,conf_call,CallAddHandler), + {error,{callback_crashed,_}} = + logger:set_handler_config(h1,conf_call,CrashHandler), + {error,{logger_process_exited,_,die}} = + logger:set_handler_config(h1,conf_call,KillHandler), + + ok = logger:remove_handler(h1), + [add,remove] = test_server:messages_get(), + + check_no_log(), + ok = logger:add_handler(h1,?MODULE,#{rem_call=>CallAddHandler}), + ok = logger:remove_handler(h1), + ok = logger:add_handler(h1,?MODULE,#{rem_call=>CrashHandler}), + ok = logger:remove_handler(h1), + ok = logger:add_handler(h1,?MODULE,#{rem_call=>KillHandler}), + ok = logger:remove_handler(h1), + [add,add,add] = test_server:messages_get(), + + ok. + +handler_failed(cleanup,_Config) -> + logger:remove_handler(h1), + logger:remove_handler(h2), + ok. + +config_sanity_check(_Config) -> + %% Primary config + {error,{invalid_config,bad}} = logger:set_primary_config(bad), + {error,{invalid_filter_default,bad}} = + logger:set_primary_config(filter_default,bad), + {error,{invalid_level,bad}} = logger:set_primary_config(level,bad), + {error,{invalid_filters,bad}} = logger:set_primary_config(filters,bad), + {error,{invalid_filter,bad}} = logger:set_primary_config(filters,[bad]), + {error,{invalid_filter,{_,_}}} = + logger:set_primary_config(filters,[{id,bad}]), + {error,{invalid_filter,{_,{_,_}}}} = + logger:set_primary_config(filters,[{id,{bad,args}}]), + {error,{invalid_filter,{_,{_,_}}}} = + logger:set_primary_config(filters,[{id,{fun() -> ok end,args}}]), + {error,{invalid_primary_config,{bad,bad}}} = + logger:set_primary_config(bad,bad), + + %% Handler config + {error,{not_found,h1}} = logger:set_handler_config(h1,a,b), + ok = logger:add_handler(h1,?MODULE,#{}), + {error,{invalid_filter_default,bad}} = + logger:set_handler_config(h1,filter_default,bad), + {error,{invalid_level,bad}} = logger:set_handler_config(h1,level,bad), + {error,{invalid_filters,bad}} = logger:set_handler_config(h1,filters,bad), + {error,{invalid_filter,bad}} = logger:set_handler_config(h1,filters,[bad]), + {error,{invalid_filter,{_,_}}} = + logger:set_handler_config(h1,filters,[{id,bad}]), + {error,{invalid_filter,{_,{_,_}}}} = + logger:set_handler_config(h1,filters,[{id,{bad,args}}]), + {error,{invalid_filter,{_,{_,_}}}} = + logger:set_handler_config(h1,filters,[{id,{fun() -> ok end,args}}]), + {error,{invalid_formatter,bad}} = + logger:set_handler_config(h1,formatter,bad), + {error,{invalid_module,{bad}}} = + logger:set_handler_config(h1,formatter,{{bad},cfg}), + {error,{invalid_formatter_config,logger_formatter,bad}} = + logger:set_handler_config(h1,formatter,{logger_formatter,bad}), + {error,{invalid_formatter_config,logger_formatter,{bad,bad}}} = + logger:set_handler_config(h1,formatter,{logger_formatter,#{bad=>bad}}), + {error,{invalid_formatter_template,logger_formatter,bad}} = + logger:set_handler_config(h1,formatter,{logger_formatter, + #{template=>bad}}), + {error,{invalid_formatter_template,logger_formatter,[1]}} = + logger:set_handler_config(h1,formatter,{logger_formatter, + #{template=>[1]}}), + ok = logger:set_handler_config(h1,formatter,{logger_formatter, + #{template=>[]}}), + {error,{invalid_formatter_config,logger_formatter,{single_line,bad}}} = + logger:set_handler_config(h1,formatter,{logger_formatter, + #{single_line=>bad}}), + ok = logger:set_handler_config(h1,formatter,{logger_formatter, + #{single_line=>true}}), + {error,{invalid_formatter_config,logger_formatter,{legacy_header,bad}}} = + logger:set_handler_config(h1,formatter,{logger_formatter, + #{legacy_header=>bad}}), + ok = logger:set_handler_config(h1,formatter,{logger_formatter, + #{legacy_header=>true}}), + {error,{invalid_formatter_config,logger_formatter,{report_cb,bad}}} = + logger:set_handler_config(h1,formatter,{logger_formatter, + #{report_cb=>bad}}), + ok = logger:set_handler_config(h1,formatter,{logger_formatter, + #{report_cb=>fun(R) -> + {"~p",[R]} + end}}), + {error,{invalid_formatter_config,logger_formatter,{chars_limit,bad}}} = + logger:set_handler_config(h1,formatter,{logger_formatter, + #{chars_limit=>bad}}), + ok = logger:set_handler_config(h1,formatter,{logger_formatter, + #{chars_limit=>unlimited}}), + ok = logger:set_handler_config(h1,formatter,{logger_formatter, + #{chars_limit=>4}}), + {error,{invalid_formatter_config,logger_formatter,{depth,bad}}} = + logger:set_handler_config(h1,formatter,{logger_formatter, + #{depth=>bad}}), + ok = logger:set_handler_config(h1,formatter,{logger_formatter, + #{depth=>unlimited}}), + ok = logger:set_handler_config(h1,formatter,{logger_formatter, + #{depth=>4}}), + {error,{invalid_formatter_config,logger_formatter,{max_size,bad}}} = + logger:set_handler_config(h1,formatter,{logger_formatter, + #{max_size=>bad}}), + ok = logger:set_handler_config(h1,formatter,{logger_formatter, + #{max_size=>unlimited}}), + ok = logger:set_handler_config(h1,formatter,{logger_formatter, + #{max_size=>4}}), + ok = logger:set_handler_config(h1,formatter,{module,config}), + {error,{callback_crashed,{error,{badmatch,3},[{?MODULE,check_config,1,_}]}}} = + logger:set_handler_config(h1,formatter,{?MODULE,crash}), + ok = logger:set_handler_config(h1,custom,custom), + + %% Old utc parameter is no longer allowed (replaced by time_offset) + {error,{invalid_formatter_config,logger_formatter,{utc,true}}} = + logger:set_handler_config(h1,formatter,{logger_formatter, + #{utc=>true}}), + {error,{invalid_formatter_config,logger_formatter,{time_offset,bad}}} = + logger:set_handler_config(h1,formatter,{logger_formatter, + #{time_offset=>bad}}), + ok = logger:set_handler_config(h1,formatter,{logger_formatter, + #{time_offset=>0}}), + ok = logger:set_handler_config(h1,formatter,{logger_formatter, + #{time_offset=>""}}), + ok = logger:set_handler_config(h1,formatter,{logger_formatter, + #{time_offset=>"Z"}}), + ok = logger:set_handler_config(h1,formatter,{logger_formatter, + #{time_offset=>"z"}}), + ok = logger:set_handler_config(h1,formatter,{logger_formatter, + #{time_offset=>"-0:0"}}), + ok = logger:set_handler_config(h1,formatter,{logger_formatter, + #{time_offset=>"+10:13"}}), + + {error,{invalid_formatter_config,logger_formatter,{time_offset,"+0"}}} = + logger:set_handler_config(h1,formatter,{logger_formatter, + #{time_offset=>"+0"}}), + + {error,{invalid_formatter_config,logger_formatter,{time_designator,bad}}} = + logger:set_handler_config(h1,formatter,{logger_formatter, + #{time_designator=>bad}}), + {error,{invalid_formatter_config,logger_formatter,{time_designator,"s"}}} = + logger:set_handler_config(h1,formatter,{logger_formatter, + #{time_designator=>"s"}}), + {error,{invalid_formatter_config,logger_formatter,{time_designator,0}}} = + logger:set_handler_config(h1,formatter,{logger_formatter, + #{time_designator=>0}}), + ok = logger:set_handler_config(h1,formatter,{logger_formatter, + #{time_designator=>$\s}}), + ok. + +config_sanity_check(cleanup,_Config) -> + logger:remove_handler(h1), + ok. + +log_failed(_Config) -> + ok = logger:add_handler(h1,?MODULE,#{level=>notice,filter_default=>log}), + {error,function_clause} = ?TRY(logger:log(bad,?map_rep)), + {error,function_clause} = ?TRY(logger:log(notice,?map_rep,bad)), + {error,function_clause} = ?TRY(logger:log(notice,fun() -> ?map_rep end,bad)), + {error,function_clause} = ?TRY(logger:log(notice,fun() -> ?map_rep end,bad,#{})), + {error,function_clause} = ?TRY(logger:log(notice,bad,bad,bad)), + {error,function_clause} = ?TRY(logger:log(notice,bad,bad,#{})), + check_no_log(), + ok = logger:log(notice,M1=?str,#{}), + check_logged(notice,M1,#{}), + ok = logger:log(notice,M2=?map_rep,#{}), + check_logged(notice,M2,#{}), + ok = logger:log(notice,M3=?keyval_rep,#{}), + check_logged(notice,M3,#{}), + + %% Should we check report input more thoroughly? + ok = logger:log(notice,M4=?keyval_rep++[other,stuff,in,list],#{}), + check_logged(notice,M4,#{}), + + %% This might break a handler since it is assumed to be a format + %% string and args, so it depends how the handler protects itself + %% against something like io_lib:format("ok","ok") + ok = logger:log(notice,"ok","ok",#{}), + check_logged(notice,"ok","ok",#{}), + + ok. + +log_failed(cleanup,_Config) -> + logger:remove_handler(h1), + ok. + +emulator(_Config) -> + ok = logger:add_handler(h1,?MODULE,#{level=>notice,filter_default=>log, + tc_proc=>self()}), + Msg = "Error in process ~p on node ~p with exit value:~n~p~n", + Error = {badmatch,4}, + Stack = [{module, function, 2, []}], + Pid = spawn(?MODULE, generate_error, [Error, Stack]), + check_logged(error, Msg, [Pid, node(), {Error, Stack}], + #{gl=>group_leader(), + error_logger=>#{tag=>error,emulator=>true}}), + ok. + +emulator(cleanup, _Config) -> + logger:remove_handler(h1), + ok. + +generate_error(Error, Stack) -> + erlang:raise(error, Error, Stack). + +via_logger_process(Config) -> + ok = logger:add_handler(h1,?MODULE,#{level=>notice,filter_default=>log, + tc_proc=>self()}), + + %% Explicitly send a message to the logger process + %% This is used by code_server, erl_prim_loader, init, prim_file, ... + Msg = ?str, + logger ! {log,error,Msg,[],#{}}, + check_logged(error, Msg, [], #{}), + + case os:type() of + {win32,_} -> + %% Skip this part on windows - cant change file mode" + ok; + _ -> + %% This should trigger the same thing from erl_prim_loader + Dir = filename:join(?config(priv_dir,Config),"dummydir"), + ok = file:make_dir(Dir), + ok = file:change_mode(Dir,8#0222), + error = erl_prim_loader:list_dir(Dir), + check_logged(error, + #{report=>"File operation error: eacces. Target: " ++ + Dir ++". Function: list_dir. "}, + #{pid=>self(), + gl=>group_leader(), + error_logger=>#{tag=>error_report, + type=>std_error}}), + ok + end. + +via_logger_process(cleanup, Config) -> + Dir = filename:join(?config(priv_dir,Config),"dummydir"), + _ = file:change_mode(Dir,8#0664), + _ = file:del_dir(Dir), + logger:remove_handler(h1), + ok. + +other_node(_Config) -> + ok = logger:add_handler(h1,?MODULE,#{level=>notice,filter_default=>log, + tc_proc=>self()}), + {ok,Node} = test_server:start_node(?FUNCTION_NAME,slave,[]), + rpc:call(Node,logger,error,[Msg=?str,#{}]), + check_logged(error,Msg,#{}), + ok. + +other_node(cleanup,_Config) -> + Nodes = nodes(), + [test_server:stop_node(Node) || Node <- Nodes], + logger:remove_handler(h1), + ok. + +compare_levels(_Config) -> + Levels = [emergency,alert,critical,error,warning,notice,info,debug], + ok = compare(Levels), + {error,badarg} = ?TRY(logger:compare_levels(bad,bad)), + {error,badarg} = ?TRY(logger:compare_levels({bad},notice)), + {error,badarg} = ?TRY(logger:compare_levels(notice,"bad")), + ok. + +compare([L|Rest]) -> + eq = logger:compare_levels(L,L), + [gt = logger:compare_levels(L,L1) || L1 <- Rest], + [lt = logger:compare_levels(L1,L) || L1 <- Rest], + compare(Rest); +compare([]) -> + ok. + +process_metadata(_Config) -> + undefined = logger:get_process_metadata(), + {error,badarg} = ?TRY(logger:set_process_metadata(bad)), + ok = logger:add_handler(h1,?MODULE,#{level=>notice,filter_default=>log}), + Time = erlang:system_time(microsecond), + ProcMeta = #{time=>Time,line=>0,custom=>proc}, + ok = logger:set_process_metadata(ProcMeta), + S1 = ?str, + ?LOG_NOTICE(S1,#{custom=>macro}), + check_logged(notice,S1,#{time=>Time,line=>0,custom=>macro}), + + Time2 = erlang:system_time(microsecond), + S2 = ?str, + ?LOG_NOTICE(S2,#{time=>Time2,line=>1,custom=>macro}), + check_logged(notice,S2,#{time=>Time2,line=>1,custom=>macro}), + + logger:notice(S3=?str,#{custom=>func}), + check_logged(notice,S3,#{time=>Time,line=>0,custom=>func}), + + ProcMeta = logger:get_process_metadata(), + ok = logger:update_process_metadata(#{custom=>changed,custom2=>added}), + Expected = ProcMeta#{custom:=changed,custom2=>added}, + Expected = logger:get_process_metadata(), + ok = logger:unset_process_metadata(), + undefined = logger:get_process_metadata(), + + ok = logger:update_process_metadata(#{custom=>added_again}), + {error,badarg} = ?TRY(logger:update_process_metadata(bad)), + #{custom:=added_again} = logger:get_process_metadata(), + + ok. + +process_metadata(cleanup,_Config) -> + logger:remove_handler(h1), + ok. + +app_config(Config) -> + %% Start a node with default configuration + {ok,_,Node} = logger_test_lib:setup(Config,[]), + + App1Name = app1, + App1 = {application, App1Name, + [{description, "Test of app with logger config"}, + {applications, [kernel]}]}, + ok = rpc:call(Node,application,load,[App1]), + ok = rpc:call(Node,application,set_env, + [App1Name,logger,[{handler,default,logger_std_h,#{}}]]), + + %% Try to add an own default handler + {error,{bad_config,{handler,{app1,{already_exist,default}}}}} = + rpc:call(Node,logger,add_handlers,[App1Name]), + + %% Add a different handler + ok = rpc:call(Node,application,set_env,[App1Name,logger, + [{handler,myh,logger_std_h,#{}}]]), + ok = rpc:call(Node,logger,add_handlers,[App1Name]), + + {ok,#{filters:=DF}} = rpc:call(Node,logger,get_handler_config,[default]), + {ok,#{filters:=[]}} = rpc:call(Node,logger,get_handler_config,[myh]), + + true = test_server:stop_node(Node), + + %% Start a node with no default handler, then add an own default handler + {ok,#{handlers:=[#{id:=simple}]},Node} = + logger_test_lib:setup(Config,[{logger,[{handler,default,undefined}]}]), + + ok = rpc:call(Node,application,load,[App1]), + ok = rpc:call(Node,application,set_env, + [App1Name,logger,[{handler,default,logger_std_h,#{}}]]), + ok = rpc:call(Node,logger,add_handlers,[App1Name]), + + #{handlers:=[#{id:=default,filters:=DF}]} = + rpc:call(Node,logger,get_config,[]), + + true = test_server:stop_node(Node), + + %% Start a silent node, then add an own default handler + {ok,#{handlers:=[]},Node} = + logger_test_lib:setup(Config,[{error_logger,silent}]), + + {error,{bad_config,{handler,[{some,bad,config}]}}} = + rpc:call(Node,logger,add_handlers,[[{some,bad,config}]]), + ok = rpc:call(Node,logger,add_handlers, + [[{handler,default,logger_std_h,#{}}]]), + + #{handlers:=[#{id:=default,filters:=DF}]} = + rpc:call(Node,logger,get_config,[]), + + ok. + +%% This test case is maintly to see code coverage. Note that +%% logger_env_var_SUITE tests a lot of the same, and checks the +%% functionality more thoroughly, but since it all happens at node +%% start, it is not possible to see code coverage in that test. +kernel_config(Config) -> + %% Start a node with simple handler only, then simulate kernel + %% start by calling internally exported + %% internal_init_logger(). This is to test all variants of kernel + %% config, including bad config, and see the code coverage. + {ok,#{handlers:=[#{id:=simple,filters:=DF}]}=LC,Node} = + logger_test_lib:setup(Config,[{error_logger,false}]), + + %% Same once more, to get coverage + ok = rpc:call(Node,logger,internal_init_logger,[]), + ok = rpc:call(Node,logger,add_handlers,[kernel]), + LC = rpc:call(Node,logger,get_config,[]), + + %% This shall mean the same as above, but using 'logger' parameter + %% instead of 'error_logger' + ok = rpc:call(Node,application,unset_env,[kernel,error_logger]), + ok = rpc:call(Node,application,set_env, + [kernel,logger,[{handler,default,undefined}]]), + ok = rpc:call(Node,logger,internal_init_logger,[]), + ok = rpc:call(Node,logger,add_handlers,[kernel]), + LC = rpc:call(Node,logger,get_config,[]), + + %% Silent + ok = rpc:call(Node,application,unset_env,[kernel,logger]), + ok = rpc:call(Node,application,set_env,[kernel,error_logger,silent]), + ok = rpc:call(Node,logger,internal_init_logger,[]), + ok = rpc:call(Node,logger,add_handlers,[kernel]), + #{primary:=#{filter_default:=log,filters:=[]}, + handlers:=[], + module_levels:=[]} = rpc:call(Node,logger,get_config,[]), + + %% Default + ok = rpc:call(Node,application,unset_env,[kernel,error_logger]), + ok = rpc:call(Node,application,unset_env,[kernel,logger]), + ok = rpc:call(Node,logger,internal_init_logger,[]), + ok = rpc:call(Node,logger,add_handlers,[kernel]), + #{primary:=#{filter_default:=log,filters:=[]}, + handlers:=[#{id:=default,filters:=DF,config:=#{type:=standard_io}}], + module_levels:=[]} = rpc:call(Node,logger,get_config,[]), + + %% error_logger=tty (same as default) + ok = rpc:call(Node,logger,remove_handler,[default]),% so it can be added again + ok = rpc:call(Node,application,set_env,[kernel,error_logger,tty]), + ok = rpc:call(Node,application,unset_env,[kernel,logger]), + ok = rpc:call(Node,logger,internal_init_logger,[]), + ok = rpc:call(Node,logger,add_handlers,[kernel]), + #{primary:=#{filter_default:=log,filters:=[]}, + handlers:=[#{id:=default,filters:=DF,config:=#{type:=standard_io}}], + module_levels:=[]} = rpc:call(Node,logger,get_config,[]), + + %% error_logger={file,File} + ok = rpc:call(Node,logger,remove_handler,[default]),% so it can be added again + F = filename:join(?config(priv_dir,Config), + atom_to_list(?FUNCTION_NAME)++".log"), + ok = rpc:call(Node,application,set_env,[kernel,error_logger,{file,F}]), + ok = rpc:call(Node,application,unset_env,[kernel,logger]), + ok = rpc:call(Node,logger,internal_init_logger,[]), + ok = rpc:call(Node,logger,add_handlers,[kernel]), + #{primary:=#{filter_default:=log,filters:=[]}, + handlers:=[#{id:=default,filters:=DF,config:=#{type:={file,F}}}], + module_levels:=[]} = rpc:call(Node,logger,get_config,[]), + + %% Same, but using 'logger' parameter instead of 'error_logger' + ok = rpc:call(Node,logger,remove_handler,[default]),% so it can be added again + ok = rpc:call(Node,application,unset_env,[kernel,error_logger]), + ok = rpc:call(Node,application,set_env,[kernel,logger, + [{handler,default,logger_std_h, + #{config=>#{type=>{file,F}}}}]]), + ok = rpc:call(Node,logger,internal_init_logger,[]), + ok = rpc:call(Node,logger,add_handlers,[kernel]), + #{primary:=#{filter_default:=log,filters:=[]}, + handlers:=[#{id:=default,filters:=DF,config:=#{type:={file,F}}}], + module_levels:=[]} = rpc:call(Node,logger,get_config,[]), + + %% Same, but with type={file,File,Modes} + ok = rpc:call(Node,logger,remove_handler,[default]),% so it can be added again + ok = rpc:call(Node,application,unset_env,[kernel,error_logger]), + M = [raw,write,delayed_write], + ok = rpc:call(Node,application,set_env,[kernel,logger, + [{handler,default,logger_std_h, + #{config=>#{type=>{file,F,M}}}}]]), + ok = rpc:call(Node,logger,internal_init_logger,[]), + ok = rpc:call(Node,logger,add_handlers,[kernel]), + #{primary:=#{filter_default:=log,filters:=[]}, + handlers:=[#{id:=default,filters:=DF,config:=#{type:={file,F,M}}}], + module_levels:=[]} = rpc:call(Node,logger,get_config,[]), + + %% Same, but with disk_log handler + ok = rpc:call(Node,logger,remove_handler,[default]),% so it can be added again + ok = rpc:call(Node,application,unset_env,[kernel,error_logger]), + M = [raw,write,delayed_write], + ok = rpc:call(Node,application,set_env,[kernel,logger, + [{handler,default,logger_disk_log_h, + #{config=>#{file=>F}}}]]), + ok = rpc:call(Node,logger,internal_init_logger,[]), + ok = rpc:call(Node,logger,add_handlers,[kernel]), + #{primary:=#{filter_default:=log,filters:=[]}, + handlers:=[#{id:=default,filters:=DF,config:=#{file:=F}}], + module_levels:=[]} = rpc:call(Node,logger,get_config,[]), + + %% Set primary filters and module level. No default handler. + ok = rpc:call(Node,logger,remove_handler,[default]),% so it can be added again + ok = rpc:call(Node,application,unset_env,[kernel,error_logger]), + ok = rpc:call(Node,application,set_env, + [kernel,logger,[{handler,default,undefined}, + {filters,stop,[{f1,{fun(_,_) -> log end,ok}}]}, + {module_level,debug,[?MODULE]}]]), + ok = rpc:call(Node,logger,internal_init_logger,[]), + ok = rpc:call(Node,logger,add_handlers,[kernel]), + #{primary:=#{filter_default:=stop,filters:=[_]}, + handlers:=[], + module_levels:=[{?MODULE,debug}]} = rpc:call(Node,logger,get_config,[]), + + %% Bad config + ok = rpc:call(Node,application,unset_env,[kernel,logger]), + + ok = rpc:call(Node,application,set_env,[kernel,error_logger,bad]), + {error,{bad_config,{kernel,{error_logger,bad}}}} = + rpc:call(Node,logger,internal_init_logger,[]), + + ok = rpc:call(Node,application,unset_env,[kernel,error_logger]), + ok = rpc:call(Node,application,set_env,[kernel,logger_level,bad]), + {error,{bad_config,{kernel,{logger_level,bad}}}} = + rpc:call(Node,logger,internal_init_logger,[]), + + ok = rpc:call(Node,application,unset_env,[kernel,logger_level]), + ok = rpc:call(Node,application,set_env, + [kernel,logger,[{filters,stop,[bad]}]]), + {error,{bad_config,{kernel,{invalid_filters,[bad]}}}} = + rpc:call(Node,logger,internal_init_logger,[]), + + ok = rpc:call(Node,application,set_env, + [kernel,logger,[{filters,stop,[bad]}]]), + {error,{bad_config,{kernel,{invalid_filters,[bad]}}}} = + rpc:call(Node,logger,internal_init_logger,[]), + + ok = rpc:call(Node,application,set_env, + [kernel,logger,[{filters,stop,[{f1,bad}]}]]), + {error,{bad_config,{kernel,{invalid_filter,{f1,bad}}}}} = + rpc:call(Node,logger,internal_init_logger,[]), + + ok = rpc:call(Node,application,set_env, + [kernel,logger,MF=[{filters,stop,[]},{filters,log,[]}]]), + {error,{bad_config,{kernel,{multiple_filters,MF}}}} = + rpc:call(Node,logger,internal_init_logger,[]), + + ok = rpc:call(Node,application,set_env, + [kernel,logger,[{module_level,bad,[?MODULE]}]]), + {error,{bad_config,{kernel,{invalid_level,bad}}}} = + rpc:call(Node,logger,internal_init_logger,[]), + + ok. + +%%%----------------------------------------------------------------- +%%% Internal +check_logged(Level,Format,Args,Meta) -> + do_check_logged(Level,{Format,Args},Meta). + +check_logged(Level,Msg,Meta) when ?IS_REPORT(Msg) -> + do_check_logged(Level,{report,Msg},Meta); +check_logged(Level,Msg,Meta) when ?IS_STRING(Msg) -> + do_check_logged(Level,{string,Msg},Meta). + +do_check_logged(Level,Msg0,Meta0) -> + receive + {#{level:=Level,msg:=Msg,meta:=Meta},_} -> + check_msg(Msg0,Msg), + check_maps(Meta0,Meta,meta) + after 500 -> + ct:fail({timeout,no_log,process_info(self(),messages)}) + end. + +check_no_log() -> + receive + X -> ct:fail({got_unexpected_log,X}) + after 500 -> + ok + end. + +check_msg(Msg,Msg) -> + ok; +check_msg({report,Expected},{report,Got}) when is_map(Expected), is_map(Got) -> + check_maps(Expected,Got,msg); +check_msg(Expected,Got) -> + ct:fail({unexpected,msg,Expected,Got}). + +check_maps(Expected,Got,What) -> + case maps:merge(Got,Expected) of + Got -> + ok; + _ -> + ct:fail({unexpected,What,Expected,Got}) + end. + +%% Handler +adding_handler(#{add_call:=Fun}) -> + Fun(); +adding_handler(Config) -> + maybe_send(add), + {ok,Config}. + +removing_handler(#{rem_call:=Fun}) -> + Fun(); +removing_handler(_Config) -> + maybe_send(remove), + ok. +changing_config(_Old,#{conf_call:=Fun}) -> + Fun(); +changing_config(_Old,Config) -> + maybe_send(changing_config), + {ok,Config}. + +maybe_send(Msg) -> + case whereis(callback_receiver) of + undefined -> ok; + Pid -> Pid ! Msg + end. + +log(_Log,#{log_call:=Fun}) -> + Fun(); +log(Log,Config) -> + TcProc = maps:get(tc_proc,Config,self()), + TcProc ! {Log,Config}, + ok. + +test_api(Level) -> + logger:Level(#{Level=>rep}), + ok = check_logged(Level,#{Level=>rep},#{}), + logger:Level(#{Level=>rep},#{my=>meta}), + ok = check_logged(Level,#{Level=>rep},#{my=>meta}), + logger:Level("~w: ~w",[Level,fa]), + ok = check_logged(Level,"~w: ~w",[Level,fa],#{}), + logger:Level("~w: ~w ~w",[Level,fa,meta],#{my=>meta}), + ok = check_logged(Level,"~w: ~w ~w",[Level,fa,meta],#{my=>meta}), + logger:Level(fun(x) -> {"~w: ~w ~w",[Level,fun_to_fa,meta]} end,x, + #{my=>meta}), + ok = check_logged(Level,"~w: ~w ~w",[Level,fun_to_fa,meta],#{my=>meta}), + logger:Level(fun(x) -> #{Level=>fun_to_r,meta=>true} end,x, + #{my=>meta}), + ok = check_logged(Level,#{Level=>fun_to_r,meta=>true},#{my=>meta}), + logger:Level(fun(x) -> <<"fun_to_s">> end,x,#{}), + ok = check_logged(Level,<<"fun_to_s">>,#{}), + logger:Level(F1=fun(x) -> {fun_to_bad} end,x,#{}), + ok = check_logged(Level,"LAZY_FUN ERROR: ~tp; Returned: ~tp", + [{F1,x},{fun_to_bad}],#{}), + logger:Level(F2=fun(x) -> erlang:error(fun_that_crashes) end,x,#{}), + ok = check_logged(Level,"LAZY_FUN CRASH: ~tp; Reason: ~tp", + [{F2,x},{error,fun_that_crashes}],#{}), + ok. + +test_log_function(Level) -> + logger:log(Level,#{Level=>rep}), + ok = check_logged(Level,#{Level=>rep},#{}), + logger:log(Level,#{Level=>rep},#{my=>meta}), + ok = check_logged(Level,#{Level=>rep},#{my=>meta}), + logger:log(Level,"~w: ~w",[Level,fa]), + ok = check_logged(Level,"~w: ~w",[Level,fa],#{}), + logger:log(Level,"~w: ~w ~w",[Level,fa,meta],#{my=>meta}), + ok = check_logged(Level,"~w: ~w ~w",[Level,fa,meta],#{my=>meta}), + logger:log(Level,fun(x) -> {"~w: ~w ~w",[Level,fun_to_fa,meta]} end, + x, #{my=>meta}), + ok = check_logged(Level,"~w: ~w ~w",[Level,fun_to_fa,meta],#{my=>meta}), + logger:log(Level,fun(x) -> #{Level=>fun_to_r,meta=>true} end, + x, #{my=>meta}), + ok = check_logged(Level,#{Level=>fun_to_r,meta=>true},#{my=>meta}), + logger:log(Level,fun(x) -> <<"fun_to_s">> end,x,#{}), + ok = check_logged(Level,<<"fun_to_s">>,#{}), + logger:log(Level,F1=fun(x) -> {fun_to_bad} end,x,#{}), + ok = check_logged(Level,"LAZY_FUN ERROR: ~tp; Returned: ~tp", + [{F1,x},{fun_to_bad}],#{}), + logger:log(Level,F2=fun(x) -> erlang:error(fun_that_crashes) end,x,#{}), + ok = check_logged(Level,"LAZY_FUN CRASH: ~tp; Reason: ~tp", + [{F2,x},{error,fun_that_crashes}],#{}), + ok. + +test_macros(emergency=Level) -> + ?LOG_EMERGENCY(#{Level=>rep}), + ok = check_logged(Level,#{Level=>rep},?MY_LOC(1)), + ?LOG_EMERGENCY(#{Level=>rep},#{my=>meta}), + ok = check_logged(Level,#{Level=>rep},(?MY_LOC(1))#{my=>meta}), + ?LOG_EMERGENCY("~w: ~w",[Level,fa]), + ok = check_logged(Level,"~w: ~w",[Level,fa],?MY_LOC(1)), + ?LOG_EMERGENCY("~w: ~w ~w",[Level,fa,meta],#{my=>meta}), + ok = check_logged(Level,"~w: ~w ~w",[Level,fa,meta],(?MY_LOC(1))#{my=>meta}), + ?LOG_EMERGENCY(fun(x) -> {"~w: ~w ~w",[Level,fun_to_fa,meta]} end, + x, #{my=>meta}), + ok = check_logged(Level,"~w: ~w ~w",[Level,fun_to_fa,meta], + (?MY_LOC(3))#{my=>meta}), + ?LOG_EMERGENCY(fun(x) -> #{Level=>fun_to_r,meta=>true} end, x, #{my=>meta}), + ok = check_logged(Level,#{Level=>fun_to_r,meta=>true}, + (?MY_LOC(2))#{my=>meta}), + ?LOG_EMERGENCY(fun(x) -> <<"fun_to_s">> end,x,#{}), + ok = check_logged(Level,<<"fun_to_s">>,?MY_LOC(1)), + F1=fun(x) -> {fun_to_bad} end, + ?LOG_EMERGENCY(F1,x,#{}), + ok = check_logged(Level,"LAZY_FUN ERROR: ~tp; Returned: ~tp", + [{F1,x},{fun_to_bad}],#{}), + F2=fun(x) -> erlang:error(fun_that_crashes) end, + ?LOG_EMERGENCY(F2,x,#{}), + ok = check_logged(Level,"LAZY_FUN CRASH: ~tp; Reason: ~tp", + [{F2,x},{error,fun_that_crashes}],#{}), + ok. + +test_log_macro(Level) -> + ?LOG(Level,#{Level=>rep}), + ok = check_logged(Level,#{Level=>rep},?MY_LOC(1)), + ?LOG(Level,#{Level=>rep},#{my=>meta}), + ok = check_logged(Level,#{Level=>rep},(?MY_LOC(1))#{my=>meta}), + ?LOG(Level,"~w: ~w",[Level,fa]), + ok = check_logged(Level,"~w: ~w",[Level,fa],?MY_LOC(1)), + ?LOG(Level,"~w: ~w ~w",[Level,fa,meta],#{my=>meta}), + ok = check_logged(Level,"~w: ~w ~w",[Level,fa,meta],(?MY_LOC(1))#{my=>meta}), + ?LOG(Level,fun(x) -> {"~w: ~w ~w",[Level,fun_to_fa,meta]} end, + x, #{my=>meta}), + ok = check_logged(Level,"~w: ~w ~w",[Level,fun_to_fa,meta], + (?MY_LOC(3))#{my=>meta}), + ?LOG(Level,fun(x) -> #{Level=>fun_to_r,meta=>true} end, x, #{my=>meta}), + ok = check_logged(Level,#{Level=>fun_to_r,meta=>true}, + (?MY_LOC(2))#{my=>meta}), + ?LOG(Level,fun(x) -> <<"fun_to_s">> end,x,#{}), + ok = check_logged(Level,<<"fun_to_s">>,?MY_LOC(1)), + F1=fun(x) -> {fun_to_bad} end, + ?LOG(Level,F1,x,#{}), + ok = check_logged(Level,"LAZY_FUN ERROR: ~tp; Returned: ~tp", + [{F1,x},{fun_to_bad}],#{}), + F2=fun(x) -> erlang:error(fun_that_crashes) end, + ?LOG(Level,F2,x,#{}), + ok = check_logged(Level,"LAZY_FUN CRASH: ~tp; Reason: ~tp", + [{F2,x},{error,fun_that_crashes}],#{}), + ok. + +%%%----------------------------------------------------------------- +%%% Called by macro ?TRY(X) +my_try(Fun) -> + try Fun() catch C:R -> {C,R} end. + +check_config(crash) -> + erlang:error({badmatch,3}); +check_config(_) -> + ok. diff --git a/lib/kernel/test/logger_disk_log_h_SUITE.erl b/lib/kernel/test/logger_disk_log_h_SUITE.erl new file mode 100644 index 0000000000..a815db14e9 --- /dev/null +++ b/lib/kernel/test/logger_disk_log_h_SUITE.erl @@ -0,0 +1,1572 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2018. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% +-module(logger_disk_log_h_SUITE). + +-compile(export_all). + +-include_lib("common_test/include/ct.hrl"). +-include_lib("kernel/include/logger.hrl"). +-include_lib("kernel/src/logger_internal.hrl"). +-include_lib("kernel/src/logger_h_common.hrl"). +-include_lib("stdlib/include/ms_transform.hrl"). +-include_lib("kernel/include/file.hrl"). + +-define(check_no_log, [] = test_server:messages_get()). + +-define(check(Expected), + receive {log,Expected} -> + [] = test_server:messages_get() + after 1000 -> + ct:fail({report_not_received, + {line,?LINE}, + {got,test_server:messages_get()}}) + end). + +-define(msg,"Log from "++atom_to_list(?FUNCTION_NAME)++ + ":"++integer_to_list(?LINE)). +-define(bin(Msg), list_to_binary(Msg++"\n")). +-define(log_no(File,N), lists:concat([File,".",N])). +-define(domain,#{domain=>[?MODULE]}). + +suite() -> + [{timetrap,{seconds,30}}, + {ct_hooks,[logger_test_lib]}]. + +init_per_suite(Config) -> + timer:start(), % to avoid progress report + Config. + +end_per_suite(_Config) -> + ok. + +init_per_group(_Group, Config) -> + Config. + +end_per_group(_Group, _Config) -> + ok. + +init_per_testcase(TestHooksCase, Config) when + TestHooksCase == write_failure; + TestHooksCase == sync_failure -> + case (fun() -> ?TEST_HOOKS_TAB == undefined end)() of + true -> + {skip,"Define the TEST_HOOKS macro to run this test"}; + false -> + ct:print("********** ~w **********", [TestHooksCase]), + Config + end; +init_per_testcase(TestCase, Config) -> + ct:print("********** ~w **********", [TestCase]), + Config. + +end_per_testcase(Case, Config) -> + try apply(?MODULE,Case,[cleanup,Config]) + catch error:undef -> ok + end, + ok. + +groups() -> + []. + +all() -> + [start_stop_handler, + create_log, + open_existing_log, + disk_log_opts, + default_formatter, + logging, + errors, + formatter_fail, + config_fail, + bad_input, + info_and_reset, + reconfig, + sync, + disk_log_full, + disk_log_wrap, + disk_log_events, + write_failure, + sync_failure, + op_switch_to_sync, + op_switch_to_drop, + op_switch_to_flush, + limit_burst_disabled, + limit_burst_enabled_one, + limit_burst_enabled_period, + kill_disabled, + qlen_kill_new, + %% qlen_kill_std, + mem_kill_new, + %% mem_kill_std, + restart_after, + handler_requests_under_load + ]. + +start_stop_handler(_Config) -> + ok = logger:add_handler(?MODULE, logger_disk_log_h, #{}), + {error,{already_exist,?MODULE}} = + logger:add_handler(?MODULE, logger_disk_log_h, #{}), + true = is_pid(whereis(h_proc_name())), + ok = logger:remove_handler(?MODULE), + timer:sleep(500), + undefined = whereis(h_proc_name()). +start_stop_handler(cleanup, _Config) -> + logger:remove_handler(?MODULE). + +create_log(Config) -> + PrivDir = ?config(priv_dir,Config), + %% test new handler + Name1 = list_to_atom(lists:concat([?FUNCTION_NAME,"_A"])), + LogFile1 = filename:join(PrivDir, Name1), + ok = start_and_add(Name1, #{filter_default=>stop, + filters=>?DEFAULT_HANDLER_FILTERS([?MODULE]), + formatter=>{?MODULE,self()}}, + #{file=>LogFile1}), + logger:notice("hello", ?domain), + logger_disk_log_h:filesync(Name1), + ct:pal("Checking contents of ~p", [?log_no(LogFile1,1)]), + try_read_file(?log_no(LogFile1,1), {ok,<<"hello\n">>}, 5000), + + %% test second handler + Name2 = list_to_atom(lists:concat([?FUNCTION_NAME,"_B"])), + DLName = lists:concat([?FUNCTION_NAME,"_B_log"]), + LogFile2 = filename:join(PrivDir, DLName), + ok = start_and_add(Name2, #{filter_default=>stop, + filters=>?DEFAULT_HANDLER_FILTERS([?MODULE]), + formatter=>{?MODULE,self()}}, + #{file=>LogFile2}), + logger:notice("dummy", ?domain), + logger_disk_log_h:filesync(Name2), + ct:pal("Checking contents of ~p", [?log_no(LogFile2,1)]), + try_read_file(?log_no(LogFile2,1), {ok,<<"dummy\n">>}, 5000), + + remove_and_stop(Name1), + remove_and_stop(Name2), + try_read_file(?log_no(LogFile1,1), {ok,<<"hello\ndummy\n">>}, 1), + try_read_file(?log_no(LogFile2,1), {ok,<<"dummy\n">>}, 5000), + ok. + +open_existing_log(Config) -> + PrivDir = ?config(priv_dir,Config), + %% test new handler + HName = ?FUNCTION_NAME, + DLName = lists:concat([?FUNCTION_NAME,"_log"]), + LogFile1 = filename:join(PrivDir, DLName), + ok = start_and_add(HName, #{filter_default=>stop, + filters=>?DEFAULT_HANDLER_FILTERS([?MODULE]), + formatter=>{?MODULE,self()}}, + #{file=>LogFile1}), + logger:notice("one", ?domain), + logger_disk_log_h:filesync(HName), + ct:pal("Checking contents of ~p", [?log_no(LogFile1,1)]), + try_read_file(?log_no(LogFile1,1), {ok,<<"one\n">>}, 5000), + logger:notice("two", ?domain), + ok = remove_and_stop(HName), + try_read_file(?log_no(LogFile1,1), {ok,<<"one\ntwo\n">>}, 5000), + + logger:notice("two and a half", ?domain), + + ok = start_and_add(HName, #{filter_default=>stop, + filters=>?DEFAULT_HANDLER_FILTERS([?MODULE]), + formatter=>{?MODULE,self()}}, + #{file=>LogFile1}), + logger:notice("three", ?domain), + logger_disk_log_h:filesync(HName), + try_read_file(?log_no(LogFile1,1), {ok,<<"one\ntwo\nthree\n">>}, 5000), + remove_and_stop(HName), + try_read_file(?log_no(LogFile1,1), {ok,<<"one\ntwo\nthree\n">>}, 5000). + +disk_log_opts(Config) -> + Get = fun(Key, PL) -> proplists:get_value(Key, PL) end, + PrivDir = ?config(priv_dir,Config), + WName = list_to_atom(lists:concat([?FUNCTION_NAME,"_W"])), + WFile = lists:concat([?FUNCTION_NAME,"_W_log"]), + Size = length("12345"), + ConfigW = #{filter_default=>stop, + filters=>?DEFAULT_HANDLER_FILTERS([?MODULE]), + formatter => {?MODULE,no_nl}}, + WFileFull = filename:join(PrivDir, WFile), + DLOptsW = #{file => WFileFull, + type => wrap, + max_no_bytes => Size, + max_no_files => 2}, + ok = start_and_add(WName, ConfigW, DLOptsW), + WInfo1 = disk_log:info(WName), + ct:log("Fullname = ~s", [WFileFull]), + {WFileFull,wrap,{Size,2},1} = {Get(file,WInfo1),Get(type,WInfo1), + Get(size,WInfo1),Get(current_file,WInfo1)}, + logger:notice("123", ?domain), + logger_disk_log_h:filesync(WName), + timer:sleep(500), + 1 = Get(current_file, disk_log:info(WName)), + + logger:notice("45", ?domain), + logger_disk_log_h:filesync(WName), + timer:sleep(500), + 1 = Get(current_file, disk_log:info(WName)), + + logger:notice("6", ?domain), + logger_disk_log_h:filesync(WName), + timer:sleep(500), + 2 = Get(current_file, disk_log:info(WName)), + + logger:notice("7890", ?domain), + logger_disk_log_h:filesync(WName), + timer:sleep(500), + 2 = Get(current_file, disk_log:info(WName)), + + HName1 = list_to_atom(lists:concat([?FUNCTION_NAME,"_H1"])), + HFile1 = lists:concat([?FUNCTION_NAME,"_H1_log"]), + ConfigH = #{filter_default=>stop, + filters=>?DEFAULT_HANDLER_FILTERS([?MODULE]), + formatter => {?MODULE,no_nl}}, + HFile1Full = filename:join(PrivDir, HFile1), + DLOptsH1 = #{file => HFile1Full, + type => halt}, + ok = start_and_add(HName1, ConfigH, DLOptsH1), + HInfo1 = disk_log:info(HName1), + ct:log("Fullname = ~s", [HFile1Full]), + {HFile1Full,halt,infinity} = {Get(file,HInfo1),Get(type,HInfo1), + Get(size,HInfo1)}, + logger:notice("12345", ?domain), + logger_disk_log_h:filesync(HName1), + timer:sleep(500), + 1 = Get(no_written_items, disk_log:info(HName1)), + + HName2 = list_to_atom(lists:concat([?FUNCTION_NAME,"_H2"])), + HFile2 = lists:concat([?FUNCTION_NAME,"_H2_log"]), + HFile2Full = filename:join(PrivDir, HFile2), + DLOptsH2 = DLOptsH1#{file => HFile2Full, + max_no_bytes => 1000}, + ok = start_and_add(HName2, ConfigH, DLOptsH2), + HInfo3 = disk_log:info(HName2), + ct:log("Fullname = ~s", [HFile2Full]), + {HFile2Full,halt,1000} = {Get(file,HInfo3),Get(type,HInfo3), + Get(size,HInfo3)}, + + remove_and_stop(WName), + remove_and_stop(HName1), + remove_and_stop(HName2), + ok. + +default_formatter(Config) -> + PrivDir = ?config(priv_dir,Config), + LogFile = filename:join(PrivDir,atom_to_list(?FUNCTION_NAME)), + HandlerConfig = #{config => #{file=>LogFile}, + filter_default=>log}, + ct:pal("Log: ~p", [LogFile]), + ok = logger:add_handler(?MODULE, logger_disk_log_h, HandlerConfig), + ok = logger:set_handler_config(?MODULE,formatter, + {?DEFAULT_FORMATTER,?DEFAULT_FORMAT_CONFIG}), + LogName = lists:concat([LogFile, ".1"]), + logger:notice("dummy"), + wait_until_written(LogName), + {ok,Bin} = file:read_file(LogName), + match = re:run(Bin, "=NOTICE REPORT====.*\ndummy", [{capture,none}]), + ok. +default_formatter(cleanup, _Config) -> + logger:remove_handler(?MODULE). + +logging(Config) -> + PrivDir = ?config(priv_dir,Config), + %% test new handler + Name = list_to_atom(lists:concat([?FUNCTION_NAME,"_1"])), + LogFile = filename:join(PrivDir, Name), + ok = start_and_add(Name, #{filter_default=>log, + formatter=>{?MODULE,self()}}, + #{file => LogFile}), + MsgFormatter = fun(Term) -> {io_lib:format("Term:~p",[Term]),[]} end, + logger:notice([{x,y}], #{report_cb => MsgFormatter}), + logger:notice([{x,y}], #{}), + ct:pal("Checking contents of ~p", [?log_no(LogFile,1)]), + try_read_file(?log_no(LogFile,1), {ok,<<"Term:[{x,y}]\n x: y\n">>}, 5000). + +logging(cleanup, _Config) -> + Name = list_to_atom(lists:concat([?FUNCTION_NAME,"_1"])), + remove_and_stop(Name). + +errors(Config) -> + PrivDir = ?config(priv_dir,Config), + Name1 = list_to_atom(lists:concat([?FUNCTION_NAME,"_1"])), + LogFile1 = filename:join(PrivDir,Name1), + HandlerConfig = #{config=>#{file=>LogFile1}, + filter_default=>log, + formatter=>{?MODULE,self()}}, + ok = logger:add_handler(Name1, logger_disk_log_h, HandlerConfig), + {error,{already_exist,Name1}} = + logger:add_handler(Name1, logger_disk_log_h, #{}), + + %%! TODO: + %%! Check how bad log_opts are handled! + + {error,{illegal_config_change,_,_}} = + logger:set_handler_config(Name1, + config, + #{file=>LogFile1, + type=>halt}), + {error,{illegal_config_change,_,_}} = + logger:set_handler_config(Name1,id,new), + + ok = logger:remove_handler(Name1), + {error,{not_found,Name1}} = logger:remove_handler(Name1), + ok. + +errors(cleanup, _Config) -> + Name1 = list_to_atom(lists:concat([?FUNCTION_NAME,"_1"])), + _ = logger:remove_handler(Name1). + +formatter_fail(Config) -> + PrivDir = ?config(priv_dir,Config), + Name = ?FUNCTION_NAME, + LogFile = filename:join(PrivDir,Name), + ct:pal("Log = ~p", [LogFile]), + HandlerConfig = #{config => #{file=>LogFile}, + filter_default=>stop, + filters=>?DEFAULT_HANDLER_FILTERS([?MODULE])}, + %% no formatter! + logger:add_handler(Name, logger_disk_log_h, HandlerConfig), + Pid = whereis(h_proc_name(Name)), + true = is_pid(Pid), + H = logger:get_handler_ids(), + true = lists:member(Name,H), + + %% Formatter is added automatically + {ok,#{formatter:={logger_formatter,_}}} = logger:get_handler_config(Name), + logger:notice(M1=?msg,?domain), + Got1 = try_match_file(?log_no(LogFile,1),"[0-9\\+\\-T:\\.]* notice: "++M1,5000), + + ok = logger:set_handler_config(Name,formatter,{nonexistingmodule,#{}}), + logger:notice(M2=?msg,?domain), + Got2 = try_match_file(?log_no(LogFile,1), + escape(Got1)++"[0-9\\+\\-T:\\.]* notice: FORMATTER CRASH: .*"++M2, + 5000), + + ok = logger:set_handler_config(Name,formatter,{?MODULE,crash}), + logger:notice(M3=?msg,?domain), + Got3 = try_match_file(?log_no(LogFile,1), + escape(Got2)++"[0-9\\+\\-T:\\.]* notice: FORMATTER CRASH: .*"++M3, + 5000), + + ok = logger:set_handler_config(Name,formatter,{?MODULE,bad_return}), + logger:notice(?msg,?domain), + try_match_file(?log_no(LogFile,1), + escape(Got3)++"FORMATTER ERROR: bad return value", + 5000), + + %% Check that handler is still alive and was never dead + Pid = whereis(h_proc_name(Name)), + H = logger:get_handler_ids(), + ok. + +formatter_fail(cleanup,_Config) -> + _ = logger:remove_handler(?FUNCTION_NAME), + ok. + +config_fail(_Config) -> + {error,{handler_not_added,{invalid_config,logger_disk_log_h,{bad,bad}}}} = + logger:add_handler(?MODULE,logger_disk_log_h, + #{config => #{bad => bad}, + filter_default=>log, + formatter=>{?MODULE,self()}}), + + {error,{handler_not_added,{invalid_levels,{_,1,_}}}} = + logger:add_handler(?MODULE,logger_disk_log_h, + #{config => #{drop_mode_qlen=>1}}), + {error,{handler_not_added,{invalid_levels,{43,42,_}}}} = + logger:add_handler(?MODULE,logger_disk_log_h, + #{config => #{sync_mode_qlen=>43, + drop_mode_qlen=>42}}), + {error,{handler_not_added,{invalid_levels,{_,43,42}}}} = + logger:add_handler(?MODULE,logger_disk_log_h, + #{config => #{drop_mode_qlen=>43, + flush_qlen=>42}}), + + ok = logger:add_handler(?MODULE,logger_disk_log_h, + #{filter_default=>log, + formatter=>{?MODULE,self()}}), + %% can't change the disk log options for a log already in use + {error,{illegal_config_change,_,_}} = + logger:set_handler_config(?MODULE,config, + #{max_no_files=>2}), + %% can't change name of an existing handler + {error,{illegal_config_change,_,_}} = + logger:set_handler_config(?MODULE,id,bad), + %% incorrect values of OP params + {ok,#{config := HConfig}} = logger:get_handler_config(?MODULE), + {error,{invalid_levels,_}} = + logger:set_handler_config(?MODULE,config, + HConfig#{sync_mode_qlen=>100, + flush_qlen=>99}), + %% invalid name of config parameter + {error,{invalid_config,logger_disk_log_h,{filesync_rep_int,2000}}} = + logger:set_handler_config(?MODULE, config, + HConfig#{filesync_rep_int => 2000}), + ok. +config_fail(cleanup,_Config) -> + logger:remove_handler(?MODULE). + +bad_input(_Config) -> + {error,{badarg,{filesync,["BadType"]}}} = + logger_disk_log_h:filesync("BadType"), + {error,{badarg,{info,["BadType"]}}} = logger_disk_log_h:info("BadType"), + {error,{badarg,{reset,["BadType"]}}} = logger_disk_log_h:reset("BadType"). + +info_and_reset(_Config) -> + ok = logger:add_handler(?MODULE,logger_disk_log_h, + #{filter_default=>log, + formatter=>{?MODULE,self()}}), + #{id := ?MODULE} = logger_disk_log_h:info(?MODULE), + ok = logger_disk_log_h:reset(?MODULE). +info_and_reset(cleanup,_Config) -> + logger:remove_handler(?MODULE). + +reconfig(Config) -> + Dir = ?config(priv_dir,Config), + ok = logger:add_handler(?MODULE, + logger_disk_log_h, + #{filter_default=>log, + filters=>?DEFAULT_HANDLER_FILTERS([?MODULE]), + formatter=>{?MODULE,self()}}), + #{id := ?MODULE, + sync_mode_qlen := ?SYNC_MODE_QLEN, + drop_mode_qlen := ?DROP_MODE_QLEN, + flush_qlen := ?FLUSH_QLEN, + burst_limit_enable := ?BURST_LIMIT_ENABLE, + burst_limit_max_count := ?BURST_LIMIT_MAX_COUNT, + burst_limit_window_time := ?BURST_LIMIT_WINDOW_TIME, + overload_kill_enable := ?OVERLOAD_KILL_ENABLE, + overload_kill_qlen := ?OVERLOAD_KILL_QLEN, + overload_kill_mem_size := ?OVERLOAD_KILL_MEM_SIZE, + overload_kill_restart_after := ?OVERLOAD_KILL_RESTART_AFTER, + filesync_repeat_interval := ?FILESYNC_REPEAT_INTERVAL, + log_opts := #{type := ?DISK_LOG_TYPE, + max_no_files := ?DISK_LOG_MAX_NO_FILES, + max_no_bytes := ?DISK_LOG_MAX_NO_BYTES, + file := _DiskLogFile}} = + logger_disk_log_h:info(?MODULE), + + {ok,#{config := HConfig0}} = logger:get_handler_config(?MODULE), + HConfig1 = HConfig0#{sync_mode_qlen => 1, + drop_mode_qlen => 2, + flush_qlen => 3, + burst_limit_enable => false, + burst_limit_max_count => 10, + burst_limit_window_time => 10, + overload_kill_enable => true, + overload_kill_qlen => 100000, + overload_kill_mem_size => 10000000, + overload_kill_restart_after => infinity, + filesync_repeat_interval => no_repeat}, + ok = logger:set_handler_config(?MODULE, config, HConfig1), + #{id := ?MODULE, + sync_mode_qlen := 1, + drop_mode_qlen := 2, + flush_qlen := 3, + burst_limit_enable := false, + burst_limit_max_count := 10, + burst_limit_window_time := 10, + overload_kill_enable := true, + overload_kill_qlen := 100000, + overload_kill_mem_size := 10000000, + overload_kill_restart_after := infinity, + filesync_repeat_interval := no_repeat} = + logger_disk_log_h:info(?MODULE), + + ok = logger:remove_handler(?MODULE), + + File = filename:join(Dir, "logfile"), + ok = logger:add_handler(?MODULE, + logger_disk_log_h, + #{filter_default=>log, + filters=>?DEFAULT_HANDLER_FILTERS([?MODULE]), + formatter=>{?MODULE,self()}, + config=> + #{type => halt, + max_no_files => 1, + max_no_bytes => 1024, + file => File}}), + #{log_opts := #{type := halt, + max_no_files := 1, + max_no_bytes := 1024, + file := File}} = + logger_disk_log_h:info(?MODULE), + ok. + +reconfig(cleanup, _Config) -> + logger:remove_handler(?MODULE). + +sync(Config) -> + Dir = ?config(priv_dir,Config), + File = filename:join(Dir, ?FUNCTION_NAME), + Log = lists:concat([File,".1"]), + ok = logger:add_handler(?MODULE, + logger_disk_log_h, + #{config => #{file => File}, + filter_default=>log, + filters=>?DEFAULT_HANDLER_FILTERS([?MODULE]), + formatter=>{?MODULE,nl}}), + + start_tracer([{disk_log,blog,2}, + {logger_disk_log_h,disk_log_sync,2}], + [{disk_log,blog,<<"first\n">>}, + {logger_disk_log_h,disk_log_sync}]), + + logger:notice("first", ?domain), + %% wait for automatic disk_log_sync + check_tracer(?FILESYNC_REPEAT_INTERVAL*2), + + %% check that if there's no repeated disk_log_sync active, + %% a disk_log_sync is still performed when handler goes idle + {ok,#{config := HConfig}} = logger:get_handler_config(?MODULE), + HConfig1 = HConfig#{filesync_repeat_interval => no_repeat}, + ok = logger:set_handler_config(?MODULE, config, HConfig1), + + no_repeat = maps:get(filesync_repeat_interval, + logger_disk_log_h:info(?MODULE)), + %% The following timer is to make sure the time from last log + %% ("first") to next ("second") is long enough, so the a flush is + %% triggered by the idle timeout between "fourth" and "fifth". + timer:sleep(?IDLE_DETECT_TIME_MSEC*2), + + start_tracer([{disk_log,blog,2}, + {logger_disk_log_h,disk_log_sync,2}], + [{disk_log,blog,<<"second\n">>}, + {logger_disk_log_h,disk_log_sync}, + {disk_log,blog,<<"third\n">>}, + {logger_disk_log_h,disk_log_sync}]), + + logger:notice("second", ?domain), + timer:sleep(?IDLE_DETECT_TIME_MSEC*2), + logger:notice("third", ?domain), + %% wait for automatic disk_log_sync + check_tracer(?IDLE_DETECT_TIME_MSEC*2), + + try_read_file(Log, {ok,<<"first\nsecond\nthird\n">>}, 1000), + + %% switch repeated disk_log_sync on and verify that the looping works + SyncInt = 1000, + WaitT = 4500, + OneSync = {logger_disk_log_h,handle_cast,repeated_disk_log_sync}, + %% receive 1 initial repeated_disk_log_sync, then 1 per sec + start_tracer([{logger_disk_log_h,handle_cast,2}], + [OneSync || _ <- lists:seq(1, 1 + trunc(WaitT/SyncInt))]), + + HConfig2 = HConfig#{filesync_repeat_interval => SyncInt}, + ok = logger:set_handler_config(?MODULE, config, HConfig2), + + SyncInt = maps:get(filesync_repeat_interval, + logger_disk_log_h:info(?MODULE)), + timer:sleep(WaitT), + HConfig3 = HConfig#{filesync_repeat_interval => no_repeat}, + ok = logger:set_handler_config(?MODULE, config, HConfig3), + check_tracer(100), + ok. +sync(cleanup,_Config) -> + dbg:stop_clear(), + logger:remove_handler(?MODULE). + +disk_log_wrap(Config) -> + Get = fun(Key, PL) -> proplists:get_value(Key, PL) end, + Dir = ?config(priv_dir,Config), + File = filename:join(Dir, ?FUNCTION_NAME), + ct:pal("Log = ~p", [File]), + MaxFiles = 3, + MaxBytes = 5, + ok = logger:add_handler(?MODULE, + logger_disk_log_h, + #{filter_default=>log, + filters=>?DEFAULT_HANDLER_FILTERS([?MODULE]), + formatter=>{?MODULE,self()}, + config=> + #{type => wrap, + max_no_files => MaxFiles, + max_no_bytes => MaxBytes, + file => File}}), + Info = disk_log:info(?MODULE), + {File,wrap,{MaxBytes,MaxFiles},1} = + {Get(file,Info),Get(type,Info),Get(size,Info),Get(current_file,Info)}, + Tester = self(), + TraceFun = fun({trace,_,call,{Mod,Func,Details}}, Pid) -> + Pid ! {trace,Mod,Func,Details}, + Pid + end, + {ok,_} = dbg:tracer(process, {TraceFun, Tester}), + {ok,_} = dbg:p(whereis(h_proc_name()), [c]), + {ok,_} = dbg:tp(logger_disk_log_h, handle_info, 2, []), + + Text = [34 + rand:uniform(126-34) || _ <- lists:seq(1,MaxBytes)], + ct:pal("String = ~p (~w)", [Text, erts_debug:size(Text)]), + %% fill first file + lists:foreach(fun(N) -> + Log = lists:concat([File,".",N]), + logger:notice(Text, ?domain), + wait_until_written(Log), + ct:pal("N = ~w", + [N = Get(current_file, + disk_log:info(?MODULE))]) + end, lists:seq(1,MaxFiles)), + + %% wait for trace messages + timer:sleep(1000), + dbg:stop_clear(), + Received = lists:flatmap(fun({trace,_M,handle_info, + [{disk_log,_Node,_Name,What},_]}) -> + [{trace,What}]; + ({log,_}) -> + [] + end, test_server:messages_get()), + ct:pal("Trace =~n~p", [Received]), + Received = [{trace,{wrap,0}} || _ <- lists:seq(1,MaxFiles-1)], + ok. + +disk_log_wrap(cleanup,_Config) -> + dbg:stop_clear(), + logger:remove_handler(?MODULE). + +disk_log_full(Config) -> + Dir = ?config(priv_dir,Config), + File = filename:join(Dir, ?FUNCTION_NAME), + ct:pal("Log = ~p", [File]), + MaxBytes = 50, + ok = logger:add_handler(?MODULE, + logger_disk_log_h, + #{filter_default=>log, + filters=>?DEFAULT_HANDLER_FILTERS([?MODULE]), + formatter=>{?MODULE,self()}, + config=> + #{type => halt, + max_no_files => 1, + max_no_bytes => MaxBytes, + file => File}}), + + Tester = self(), + TraceFun = fun({trace,_,call,{Mod,Func,Details}}, Pid) -> + Pid ! {trace,Mod,Func,Details}, + Pid + end, + {ok,_} = dbg:tracer(process, {TraceFun, Tester}), + {ok,_} = dbg:p(whereis(h_proc_name()), [c]), + {ok,_} = dbg:tp(logger_disk_log_h, handle_info, 2, []), + + NoOfChars = 5, + Text = [34 + rand:uniform(126-34) || _ <- lists:seq(1,NoOfChars)], + [logger:notice(Text, ?domain) || _ <- lists:seq(1,trunc(MaxBytes/NoOfChars)+1)], + + %% wait for trace messages + timer:sleep(2000), + dbg:stop_clear(), + Received = lists:flatmap(fun({trace,_M,handle_info, + [{disk_log,_Node,_Name,What},_]}) -> + [{trace,What}]; + ({log,_}) -> + [] + end, test_server:messages_get()), + ct:pal("Trace =~n~p", [Received]), + [{trace,full}, + {trace,{error_status,{error,{full,_}}}}] = Received, + ok. +disk_log_full(cleanup, _Config) -> + dbg:stop_clear(), + logger:remove_handler(?MODULE). + +disk_log_events(Config) -> + Node = node(), + Log = ?MODULE, + ok = logger:add_handler(?MODULE, + logger_disk_log_h, + #{filter_default=>log, + filters=>?DEFAULT_HANDLER_FILTERS([?MODULE]), + formatter=>{?MODULE,self()}}), + + %% Events copied from disk_log API + Events = + [{disk_log, Node, Log, {wrap, 0}}, + {disk_log, Node, Log, {truncated, 0}}, + {disk_log, Node, Log, {read_only, 42}}, + {disk_log, Node, Log, {blocked_log, 42}}, + {disk_log, Node, Log, {format_external, 42}}, + {disk_log, Node, Log, full}, + {disk_log, Node, Log, {error_status, ok}}], + + Tester = self(), + TraceFun = fun({trace,_,call,{Mod,Func,Details}}, Pid) -> + Pid ! {trace,Mod,Func,Details}, + Pid + end, + {ok,_} = dbg:tracer(process, {TraceFun, Tester}), + {ok,_} = dbg:p(whereis(h_proc_name()), [c]), + {ok,_} = dbg:tp(logger_disk_log_h, handle_info, 2, []), + + [whereis(h_proc_name()) ! E || E <- Events], + %% wait for trace messages + timer:sleep(2000), + dbg:stop_clear(), + Received = lists:map(fun({trace,_M,handle_info, + [Got,_]}) -> Got + end, test_server:messages_get()), + ct:pal("Trace =~n~p", [Received]), + NoOfEvents = length(Events), + NoOfEvents = length(Received), + lists:foreach(fun(Event) -> + true = lists:member(Event, Received) + end, Received), + ok. +disk_log_events(cleanup, _Config) -> + dbg:stop_clear(), + logger:remove_handler(?MODULE). + +write_failure(Config) -> + Dir = ?config(priv_dir, Config), + File = filename:join(Dir, ?FUNCTION_NAME), + Log = lists:concat([File,".1"]), + ct:pal("Log = ~p", [Log]), + + Node = start_h_on_new_node(Config, File), + false = (undefined == rpc:call(Node, ets, whereis, [?TEST_HOOKS_TAB])), + rpc:call(Node, ets, insert, [?TEST_HOOKS_TAB,{tester,self()}]), + rpc:call(Node, ?MODULE, set_internal_log, [?MODULE,internal_log]), + rpc:call(Node, ?MODULE, set_result, [disk_log_blog,ok]), + HState = rpc:call(Node, logger_disk_log_h, info, [?STANDARD_HANDLER]), + ct:pal("LogOpts = ~p", [LogOpts = maps:get(log_opts, HState)]), + + ok = log_on_remote_node(Node, "Logged1"), + rpc:call(Node, logger_disk_log_h, filesync, [?STANDARD_HANDLER]), + ?check_no_log, + + SyncRepInt = case (fun() -> is_atom(?FILESYNC_REPEAT_INTERVAL) end)() of + true -> 5500; + false -> ?FILESYNC_REPEAT_INTERVAL + 500 + end, + + try_read_file(Log, {ok,<<"Logged1\n">>}, SyncRepInt), + + rpc:call(Node, ?MODULE, set_result, [disk_log_blog,{error,no_such_log}]), + ok = log_on_remote_node(Node, "Cause simple error printout"), + + ?check({error,{?STANDARD_HANDLER,log,LogOpts,{error,no_such_log}}}), + + ok = log_on_remote_node(Node, "No second error printout"), + ?check_no_log, + + rpc:call(Node, ?MODULE, set_result, [disk_log_blog, + {error,{full,?STANDARD_HANDLER}}]), + ok = log_on_remote_node(Node, "Cause simple error printout"), + ?check({error,{?STANDARD_HANDLER,log,LogOpts, + {error,{full,?STANDARD_HANDLER}}}}), + + rpc:call(Node, ?MODULE, set_result, [disk_log_blog,ok]), + ok = log_on_remote_node(Node, "Logged2"), + rpc:call(Node, logger_disk_log_h, filesync, [?STANDARD_HANDLER]), + ?check_no_log, + try_read_file(Log, {ok,<<"Logged1\nLogged2\n">>}, SyncRepInt), + ok. +write_failure(cleanup, _Config) -> + Nodes = nodes(), + [test_server:stop_node(Node) || Node <- Nodes]. + + +sync_failure(Config) -> + Dir = ?config(priv_dir, Config), + FileName = lists:concat([?MODULE,"_",?FUNCTION_NAME]), + File = filename:join(Dir, FileName), + + + Node = start_h_on_new_node(Config, File), + false = (undefined == rpc:call(Node, ets, whereis, [?TEST_HOOKS_TAB])), + rpc:call(Node, ets, insert, [?TEST_HOOKS_TAB,{tester,self()}]), + rpc:call(Node, ?MODULE, set_internal_log, [?MODULE,internal_log]), + rpc:call(Node, ?MODULE, set_result, [disk_log_sync,ok]), + HState = rpc:call(Node, logger_disk_log_h, info, [?STANDARD_HANDLER]), + LogOpts = maps:get(log_opts, HState), + + SyncInt = 500, + ok = rpc:call(Node, logger, set_handler_config, + [?STANDARD_HANDLER, config, + #{filesync_repeat_interval => SyncInt}]), + Info = rpc:call(Node, logger_disk_log_h, info, [?STANDARD_HANDLER]), + SyncInt = maps:get(filesync_repeat_interval, Info), + + ok = log_on_remote_node(Node, "Logged1"), + ?check_no_log, + + rpc:call(Node, ?MODULE, set_result, [disk_log_sync,{error,no_such_log}]), + ok = log_on_remote_node(Node, "Cause simple error printout"), + + ?check({error,{?STANDARD_HANDLER,filesync,LogOpts,{error,no_such_log}}}), + + ok = log_on_remote_node(Node, "No second error printout"), + ?check_no_log, + + rpc:call(Node, ?MODULE, set_result, + [disk_log_sync,{error,{blocked_log,?STANDARD_HANDLER}}]), + ok = log_on_remote_node(Node, "Cause simple error printout"), + ?check({error,{?STANDARD_HANDLER,filesync,LogOpts, + {error,{blocked_log,?STANDARD_HANDLER}}}}), + + rpc:call(Node, ?MODULE, set_result, [disk_log_sync,ok]), + ok = log_on_remote_node(Node, "Logged2"), + ?check_no_log, + ok. +sync_failure(cleanup, _Config) -> + Nodes = nodes(), + [test_server:stop_node(Node) || Node <- Nodes]. + +start_h_on_new_node(Config, File) -> + {ok,_,Node} = + logger_test_lib:setup( + Config, + [{logger,[{handler,default,logger_disk_log_h, + #{ config => #{ file => File }}}]}]), + ok = rpc:call(Node,logger,set_handler_config,[?STANDARD_HANDLER,formatter, + {?MODULE,nl}]), + Node. + +log_on_remote_node(Node,Msg) -> + _ = spawn_link(Node, + fun() -> erlang:group_leader(whereis(user),self()), + logger:notice(Msg) + end), + ok. + +%% functions for test hook macros to be called by rpc +set_internal_log(_Mod, _Func) -> + ?set_internal_log({_Mod,_Func}). +set_result(_Op, _Result) -> + ?set_result(_Op, _Result). +set_defaults() -> + ?set_defaults(). + +%% internal log function that sends the term to the test case process +internal_log(Type, Term) -> + [{tester,Tester}] = ets:lookup(?TEST_HOOKS_TAB, tester), + Tester ! {log,{Type,Term}}, + logger:internal_log(Type, Term), + ok. + + +%%%----------------------------------------------------------------- +%%% Overload protection tests + +op_switch_to_sync(Config) -> + {Log,HConfig,DLHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), + NumOfReqs = 500, + NewHConfig = + HConfig#{config => DLHConfig#{sync_mode_qlen => 2, + drop_mode_qlen => NumOfReqs+1, + flush_qlen => 2*NumOfReqs, + burst_limit_enable => false}}, + ok = logger:set_handler_config(?MODULE, NewHConfig), + send_burst({n,NumOfReqs}, seq, {chars,79}, notice), + Lines = count_lines(Log), + NumOfReqs = Lines, + ok = file_delete(Log), + ok. +op_switch_to_sync(cleanup, _Config) -> + ok = stop_handler(?MODULE). + +op_switch_to_drop() -> + [{timetrap,{seconds,180}}]. +op_switch_to_drop(Config) -> + Test = + fun() -> + {Log,HConfig,DLHConfig} = + start_handler(?MODULE, ?FUNCTION_NAME, Config), + NumOfReqs = 300, + Procs = 2, + Bursts = 10, + NewHConfig = + HConfig#{config => + DLHConfig#{sync_mode_qlen => 1, + drop_mode_qlen => 2, + flush_qlen => Procs*NumOfReqs*Bursts, + burst_limit_enable => false}}, + ok = logger:set_handler_config(?MODULE, NewHConfig), + %% It sometimes happens that the handler either gets + %% the requests in a slow enough pace so that dropping + %% never occurs. Therefore, lets generate a number of + %% bursts to increase the chance of message buildup. + [send_burst({n,NumOfReqs}, {spawn,Procs,0}, {chars,79}, notice) || + _ <- lists:seq(1, Bursts)], + Logged = count_lines(Log), + ok = stop_handler(?MODULE), + ct:pal("Number of messages dropped = ~w (~w)", + [Procs*NumOfReqs*Bursts-Logged,Procs*NumOfReqs*Bursts]), + true = (Logged < (Procs*NumOfReqs*Bursts)), + true = (Logged > 0), + _ = file_delete(Log), + ok + end, + %% As it's tricky to get the timing right in only one go, we perform the + %% test repeatedly, hoping that will generate a successful result. + case repeat_until_ok(Test, 10) of + {ok,{Failures,_Result}} -> + ct:log("Failed ~w times before success!", [Failures]); + {fails,Reason} -> + ct:fail(Reason) + end. +op_switch_to_drop(cleanup, _Config) -> + _ = stop_handler(?MODULE). + +op_switch_to_flush() -> + [{timetrap,{minutes,3}}]. +op_switch_to_flush(Config) -> + Test = + fun() -> + {Log,HConfig,DLHConfig} = + start_handler(?MODULE, ?FUNCTION_NAME, Config), + + %% NOTE: it's important that both async and sync + %% requests have been queued when the flush happens + %% (verify with coverage of flush_log_requests/2) + + NewHConfig = + HConfig#{config => + DLHConfig#{sync_mode_qlen => 2, + %% disable drop mode + drop_mode_qlen => 300, + flush_qlen => 300, + burst_limit_enable => false}}, + ok = logger:set_handler_config(?MODULE, NewHConfig), + NumOfReqs = 1500, + Procs = 10, + Bursts = 10, + %% It sometimes happens that the handler either gets + %% the requests in a slow enough pace so that flushing + %% never occurs, or it gets all messages at once, + %% causing all messages to get flushed (no dropping of + %% sync messages gets tested). Therefore, lets + %% generate a number of bursts to increase the chance + %% of message buildup in some random fashion. + [send_burst({n,NumOfReqs}, {spawn,Procs,0}, {chars,79}, notice) || + _ <- lists:seq(1,Bursts)], + Logged = count_lines(Log), + ok= stop_handler(?MODULE), + ct:pal("Number of messages flushed/dropped = ~w (~w)", + [NumOfReqs*Procs*Bursts-Logged,NumOfReqs*Procs*Bursts]), + true = (Logged < (NumOfReqs*Procs*Bursts)), + true = (Logged > 0), + _ = file_delete(Log), + ok + end, + %% As it's tricky to get the timing right in only one go, we perform the + %% test repeatedly, hoping that will generate a successful result. + case repeat_until_ok(Test, 10) of + {ok,{Failures,_Result}} -> + ct:log("Failed ~w times before success!", [Failures]); + {fails,Reason} -> + ct:fail(Reason) + end. +op_switch_to_flush(cleanup, _Config) -> + _ = stop_handler(?MODULE). + + +limit_burst_disabled(Config) -> + {Log,HConfig,DLHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), + NewHConfig = + HConfig#{config => DLHConfig#{burst_limit_enable => false, + burst_limit_max_count => 10, + burst_limit_window_time => 2000, + drop_mode_qlen => 200, + flush_qlen => 300}}, + ok = logger:set_handler_config(?MODULE, NewHConfig), + NumOfReqs = 100, + send_burst({n,NumOfReqs}, seq, {chars,79}, notice), + Logged = count_lines(Log), + ct:pal("Number of messages logged = ~w", [Logged]), + NumOfReqs = Logged, + ok = file_delete(Log), + ok. +limit_burst_disabled(cleanup, _Config) -> + ok = stop_handler(?MODULE). + +limit_burst_enabled_one(Config) -> + {Log,HConfig,DLHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), + ReqLimit = 10, + NewHConfig = + HConfig#{config => DLHConfig#{burst_limit_enable => true, + burst_limit_max_count => ReqLimit, + burst_limit_window_time => 2000, + drop_mode_qlen => 200, + flush_qlen => 300}}, + ok = logger:set_handler_config(?MODULE, NewHConfig), + NumOfReqs = 100, + send_burst({n,NumOfReqs}, seq, {chars,79}, notice), + Logged = count_lines(Log), + ct:pal("Number of messages logged = ~w", [Logged]), + ReqLimit = Logged, + ok = file_delete(Log), + ok. +limit_burst_enabled_one(cleanup, _Config) -> + ok = stop_handler(?MODULE). + +limit_burst_enabled_period(Config) -> + {Log,HConfig,DLHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), + ReqLimit = 10, + BurstTWin = 1000, + NewHConfig = + HConfig#{config => DLHConfig#{burst_limit_enable => true, + burst_limit_max_count => ReqLimit, + burst_limit_window_time => BurstTWin, + drop_mode_qlen => 20000, + flush_qlen => 20001}}, + ok = logger:set_handler_config(?MODULE, NewHConfig), + + Windows = 3, + Sent = send_burst({t,BurstTWin*Windows}, seq, {chars,79}, notice), + Logged = count_lines(Log), + ct:pal("Number of messages sent = ~w~nNumber of messages logged = ~w", + [Sent,Logged]), + true = (Logged > (ReqLimit*Windows)) andalso + (Logged < (ReqLimit*(Windows+2))), + ok = file_delete(Log), + ok. +limit_burst_enabled_period(cleanup, _Config) -> + ok = stop_handler(?MODULE). + +kill_disabled(Config) -> + {Log,HConfig,DLHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), + NewHConfig = + HConfig#{config=>DLHConfig#{overload_kill_enable=>false, + overload_kill_qlen=>10, + overload_kill_mem_size=>100}}, + ok = logger:set_handler_config(?MODULE, NewHConfig), + NumOfReqs = 100, + send_burst({n,NumOfReqs}, seq, {chars,79}, notice), + Logged = count_lines(Log), + ct:pal("Number of messages logged = ~w", [Logged]), + ok = file_delete(Log), + true = is_pid(whereis(h_proc_name())), + ok. +kill_disabled(cleanup, _Config) -> + ok = stop_handler(?MODULE). + +qlen_kill_new(Config) -> + {Log,HConfig,DLHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), + Pid0 = whereis(h_proc_name()), + {_,Mem0} = process_info(Pid0, memory), + RestartAfter = ?OVERLOAD_KILL_RESTART_AFTER, + NewHConfig = + HConfig#{config => + DLHConfig#{overload_kill_enable=>true, + overload_kill_qlen=>10, + overload_kill_mem_size=>Mem0+50000, + overload_kill_restart_after=>RestartAfter}}, + ok = logger:set_handler_config(?MODULE, NewHConfig), + MRef = erlang:monitor(process, Pid0), + NumOfReqs = 100, + Procs = 4, + send_burst({n,NumOfReqs}, {spawn,Procs,0}, {chars,79}, notice), + %% send_burst({n,NumOfReqs}, seq, {chars,79}, notice), + receive + {'DOWN', MRef, _, _, Info} -> + case Info of + {shutdown,{overloaded,?MODULE,QLen,Mem}} -> + ct:pal("Terminated with qlen = ~w, mem = ~w", [QLen,Mem]); + killed -> + ct:pal("Slow shutdown, handler process was killed!", []) + end, + file_delete(Log), + {ok,_} = wait_for_process_up(RestartAfter * 3), + ok + after + 5000 -> + Info = logger_disk_log_h:info(?MODULE), + ct:pal("Handler state = ~p", [Info]), + ct:fail("Handler not dead! It should not have survived this!") + end. +qlen_kill_new(cleanup, _Config) -> + ok = stop_handler(?MODULE). + +mem_kill_new(Config) -> + {Log,HConfig,DLHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), + Pid0 = whereis(h_proc_name()), + {_,Mem0} = process_info(Pid0, memory), + RestartAfter = ?OVERLOAD_KILL_RESTART_AFTER, + NewHConfig = + HConfig#{config => + DLHConfig#{overload_kill_enable=>true, + overload_kill_qlen=>50000, + overload_kill_mem_size=>Mem0+500, + overload_kill_restart_after=>RestartAfter}}, + ok = logger:set_handler_config(?MODULE, NewHConfig), + MRef = erlang:monitor(process, Pid0), + NumOfReqs = 100, + Procs = 4, + send_burst({n,NumOfReqs}, {spawn,Procs,0}, {chars,79}, notice), + %% send_burst({n,NumOfReqs}, seq, {chars,79}, notice), + receive + {'DOWN', MRef, _, _, Info} -> + case Info of + {shutdown,{overloaded,?MODULE,QLen,Mem}} -> + ct:pal("Terminated with qlen = ~w, mem = ~w", [QLen,Mem]); + killed -> + ct:pal("Slow shutdown, handler process was killed!", []) + end, + file_delete(Log), + {ok,_} = wait_for_process_up(RestartAfter * 3), + ok + after + 5000 -> + Info = logger_disk_log_h:info(?MODULE), + ct:pal("Handler state = ~p", [Info]), + ct:fail("Handler not dead! It should not have survived this!") + end. +mem_kill_new(cleanup, _Config) -> + ok = stop_handler(?MODULE). + +restart_after() -> + [{timetrap,{minutes,2}}]. +restart_after(Config) -> + {Log,HConfig,DLHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), + NewHConfig1 = + HConfig#{config=>DLHConfig#{overload_kill_enable=>true, + overload_kill_qlen=>10, + overload_kill_restart_after=>infinity}}, + ok = logger:set_handler_config(?MODULE, NewHConfig1), + MRef1 = erlang:monitor(process, whereis(h_proc_name())), + %% kill handler + send_burst({n,100}, {spawn,4,0}, {chars,79}, notice), + receive + {'DOWN', MRef1, _, _, _Reason1} -> + file_delete(Log), + error = wait_for_process_up(?OVERLOAD_KILL_RESTART_AFTER * 3), + ok + after + 5000 -> + Info1 = logger_std_h:info(?MODULE), + ct:pal("Handler state = ~p", [Info1]), + ct:fail("Handler not dead! It should not have survived this!") + end, + + {Log,_,_} = start_handler(?MODULE, ?FUNCTION_NAME, Config), + RestartAfter = ?OVERLOAD_KILL_RESTART_AFTER, + NewHConfig2 = + HConfig#{config=>DLHConfig#{overload_kill_enable=>true, + overload_kill_qlen=>10, + overload_kill_restart_after=>RestartAfter}}, + ok = logger:set_handler_config(?MODULE, NewHConfig2), + Pid0 = whereis(h_proc_name()), + MRef2 = erlang:monitor(process, Pid0), + %% kill handler + send_burst({n,100}, {spawn,4,0}, {chars,79}, notice), + receive + {'DOWN', MRef2, _, _, _Reason2} -> + file_delete(Log), + {ok,Pid1} = wait_for_process_up(RestartAfter * 3), + false = (Pid1 == Pid0), + ok + after + 5000 -> + Info2 = logger_std_h:info(?MODULE), + ct:pal("Handler state = ~p", [Info2]), + ct:fail("Handler not dead! It should not have survived this!") + end, + ok. +restart_after(cleanup, _Config) -> + ok = stop_handler(?MODULE). + +%% send handler requests (sync, info, reset, change_config) +%% during high load to verify that sync, dropping and flushing is +%% handled correctly. +handler_requests_under_load() -> + [{timetrap,{minutes,5}}]. +handler_requests_under_load(Config) -> + {Log,HConfig,DLHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), + NewHConfig = + HConfig#{config => DLHConfig#{sync_mode_qlen => 2, + drop_mode_qlen => 1000, + flush_qlen => 2000, + burst_limit_enable => false}}, + ok = logger:set_handler_config(?MODULE, NewHConfig), + Pid = spawn_link(fun() -> send_requests(?MODULE, 1, [{filesync,[]}, + {info,[]}, + {reset,[]}, + {change_config,[]}]) + end), + Procs = 100, + Sent = Procs * send_burst({n,5000}, {spawn,Procs,10}, {chars,79}, notice), + Pid ! {self(),finish}, + ReqResult = receive {Pid,Result} -> Result end, + Logged = count_lines(Log), + ct:pal("Number of messages sent = ~w~nNumber of messages logged = ~w", + [Sent,Logged]), + FindError = fun(Res) -> + [E || E <- Res, + is_tuple(E) andalso (element(1,E) == error)] + end, + Errors = [{Req,FindError(Res)} || {Req,Res} <- ReqResult], + NoOfReqs = lists:foldl(fun({_,Res}, N) -> N + length(Res) end, 0, ReqResult), + ct:pal("~w requests made. Errors: ~n~p", [NoOfReqs,Errors]), + ok = file_delete(Log). +handler_requests_under_load(cleanup, _Config) -> + ok = stop_handler(?MODULE). + +send_requests(HName, TO, Reqs = [{Req,Res}|Rs]) -> + receive + {From,finish} -> + From ! {self(),Reqs} + after + TO -> + Result = + case Req of + change_config -> + logger:set_handler_config(HName, logger_disk_log_h, + #{overload_kill_enable => + false}); + Func -> + logger_disk_log_h:Func(HName) + end, + send_requests(HName, TO, Rs ++ [{Req,[Result|Res]}]) + end. + +%%%----------------------------------------------------------------- +%%% +start_handler(Name, FuncName, Config) -> + Dir = ?config(priv_dir,Config), + File = filename:join(Dir, FuncName), + ct:pal("Logging to ~tp", [File]), + FullFile = lists:concat([File,".1"]), + _ = file_delete(FullFile), + ok = logger:add_handler(Name, + logger_disk_log_h, + #{config=>#{file => File, + max_no_files => 1, + max_no_bytes => 100000000}, + filter_default=>log, + filters=>?DEFAULT_HANDLER_FILTERS([Name]), + formatter=>{?MODULE,op}}), + {ok,HConfig = #{config := DLHConfig}} = logger:get_handler_config(Name), + {FullFile,HConfig,DLHConfig}. + +stop_handler(Name) -> + ct:pal("Stopping handler ~p!", [Name]), + logger:remove_handler(Name). + +send_burst(NorT, Type, {chars,Sz}, Class) -> + Text = [34 + rand:uniform(126-34) || _ <- lists:seq(1,Sz)], + case NorT of + {n,N} -> + %% process_flag(priority, high), + send_n_burst(N, Type, Text, Class), + %% process_flag(priority, normal), + N; + {t,T} -> + ct:pal("Sending messages sequentially for ~w ms", [T]), + T0 = erlang:monotonic_time(millisecond), + send_t_burst(T0, T, Text, Class, 0) + end. + +send_n_burst(0, _, _Text, _Class) -> + ok; +send_n_burst(N, seq, Text, Class) -> + ok = logger:Class(Text, ?domain), + send_n_burst(N-1, seq, Text, Class); +send_n_burst(N, {spawn,Ps,TO}, Text, Class) -> + ct:pal("~w processes each sending ~w messages", [Ps,N]), + MRefs = [begin if TO == 0 -> ok; true -> timer:sleep(TO) end, + monitor(process,spawn_link(per_proc_fun(N,Text,Class,X))) + end || X <- lists:seq(1,Ps)], + lists:foreach(fun(MRef) -> + receive + {'DOWN', MRef, _, _, _} -> + ok + end + end, MRefs), + ct:pal("Message burst sent", []), + ok. + +send_t_burst(T0, T, Text, Class, N) -> + T1 = erlang:monotonic_time(millisecond), + if (T1-T0) > T -> + N; + true -> + ok = logger:Class(Text, ?domain), + send_t_burst(T0, T, Text, Class, N+1) + end. + +per_proc_fun(N,Text,Class,X) when X rem 2 == 0 -> + fun() -> + process_flag(priority,high), + send_n_burst(N, seq, Text, Class) + end; +per_proc_fun(N,Text,Class,_) -> + fun() -> + send_n_burst(N, seq, Text, Class) + end. + +%%%----------------------------------------------------------------- +%%% Formatter callback +%%% Using this to send the formatted string back to the test case +%%% process - so it can check for logged events. +format(_,bad_return) -> + bad_return; +format(_,crash) -> + erlang:error(formatter_crashed); +format(#{msg:={report,R},meta:=#{report_cb:=Fun}}=Log,Config) -> + format(Log#{msg=>Fun(R)},Config); +format(#{msg:={string,String0}},no_nl) -> + String = unicode:characters_to_list(String0), + String; +format(#{msg:={string,String0}},nl) -> + String = unicode:characters_to_list(String0), + String++"\n"; +format(#{msg:={string,String0}},op) -> + String = unicode:characters_to_list(String0), + String++"\n"; +format(#{msg:={report,#{label:={supervisor,progress}}}},op) -> + ""; +format(#{msg:={report,#{label:={gen_server,terminate}}}},op) -> + ""; +format(#{msg:={report,#{label:={proc_lib,crash}}}},op) -> + ""; +format(#{msg:={F,A}},OpOrPid) when is_list(F), is_list(A) -> + String = lists:flatten(io_lib:format(F,A)), + if is_pid(OpOrPid) -> OpOrPid ! {log,String}; + true -> ok + end, + String++"\n"; +format(#{msg:={string,String0}},Pid) -> + String = unicode:characters_to_list(String0), + Pid ! {log,String}, + String++"\n"; +format(Msg,Tag) -> + Error = {unexpected_format,Msg,Tag}, + erlang:display(Error), + exit(Error). + +remove(Handler, LogName) -> + logger_disk_log_h:remove(Handler, LogName), + HState = #{log_names := Logs} = logger_disk_log_h:info(), + false = maps:is_key(LogName, HState), + false = lists:member(LogName, Logs), + false = logger_config:exist(?LOGGER_TABLE, LogName), + {error,no_such_log} = disk_log:info(LogName), + ok. + +start_and_add(Name, Config, LogOpts) -> + HConfig = maps:get(config, Config, #{}), + HConfig1 = maps:merge(HConfig, LogOpts), + Config1 = Config#{config=>HConfig1}, + ct:pal("Adding handler ~w with: ~p", [Name,Config1]), + ok = logger:add_handler(Name, logger_disk_log_h, Config1), + Pid = whereis(h_proc_name(Name)), + true = is_pid(Pid), + Name = proplists:get_value(name, disk_log:info(Name)), + ok. + +remove_and_stop(Handler) -> + ok = logger:remove_handler(Handler), + timer:sleep(500), + undefined = whereis(h_proc_name(Handler)), + ok. + +try_read_file(FileName, Expected, Time) -> + try_read_file(FileName, Expected, Time, undefined). + +try_read_file(FileName, Expected, Time, _) when Time > 0 -> + case file:read_file(FileName) of + Expected -> + ok; + Error = {error,_Reason} -> + erlang:error(Error); + SomethingElse -> + ct:pal("try_read_file read unexpected: ~p~n", [SomethingElse]), + timer:sleep(500), + try_read_file(FileName, Expected, Time-500, SomethingElse) + end; + +try_read_file(_, _, _, Incorrect) -> + ct:pal("try_read_file got incorrect pattern: ~p~n", [Incorrect]), + erlang:error({error,not_matching_pattern,Incorrect}). + +try_match_file(FileName, Pattern, Time) -> + try_match_file(FileName, Pattern, Time, <<>>). + +try_match_file(FileName, Pattern, Time, _) when Time > 0 -> + case file:read_file(FileName) of + {ok, Bin} -> + case re:run(Bin,Pattern,[{capture,none}]) of + match -> + unicode:characters_to_list(Bin); + _ -> + timer:sleep(100), + try_match_file(FileName, Pattern, Time-100, Bin) + end; + Error -> + erlang:error(Error) + end; +try_match_file(_,Pattern,_,Incorrect) -> + ct:pal("try_match_file did not match pattern: ~p~nGot: ~p~n", + [Pattern,Incorrect]), + erlang:error({error,not_matching_pattern,Pattern,Incorrect}). + +count_lines(File) -> + wait_until_written(File), + count_lines1(File). + +wait_until_written(File) -> + wait_until_written(File, -1). + +wait_until_written(File, Sz) -> + timer:sleep(2000), + case file:read_file_info(File) of + {ok,#file_info{size = Sz}} -> + timer:sleep(1000), + case file:read_file_info(File) of + {ok,#file_info{size = Sz}} -> + ok; + {ok,#file_info{size = Sz1}} -> + wait_until_written(File, Sz1) + end; + {ok,#file_info{size = Sz1}} -> + wait_until_written(File, Sz1) + end. + +count_lines1(File) -> + {_,Dev} = file:open(File, [read]), + Lines = count_lines2(Dev, 0), + file:close(Dev), + Lines. + +count_lines2(Dev, LC) -> + case file:read_line(Dev) of + {ok,"Handler logger_disk_log_h_SUITE " ++_} -> + %% Not counting handler info + count_lines2(Dev,LC); + {ok,_} -> + count_lines2(Dev,LC+1); + eof -> LC + end. + +repeat_until_ok(Fun, N) -> + repeat_until_ok(Fun, 0, N, undefined). + +repeat_until_ok(_Fun, Stop, Stop, Reason) -> + {fails,Reason}; + +repeat_until_ok(Fun, C, Stop, FirstReason) -> + if C > 0 -> timer:sleep(5000); + true -> ok + end, + try Fun() of + Result -> + {ok,{C,Result}} + catch + _:Reason:Stack -> + ct:pal("Test fails: ~p (~p)~n", [Reason,hd(Stack)]), + if FirstReason == undefined -> + repeat_until_ok(Fun, C+1, Stop, {Reason,Stack}); + true -> + repeat_until_ok(Fun, C+1, Stop, FirstReason) + end + end. + +start_tracer(Trace,Expected) -> + Pid = self(), + dbg:tracer(process,{fun tracer/2,{Pid,Expected}}), + dbg:p(h_proc_name(),[c]), + tpl(Trace), + ok. + +tpl([{M,F,A}|Trace]) -> + {ok,Match} = dbg:tpl(M,F,A,c), + case lists:keyfind(matched,1,Match) of + {_,_,1} -> + ok; + _ -> + dbg:stop_clear(), + throw({skip,"Can't trace "++atom_to_list(M)++":"++ + atom_to_list(F)++"/"++integer_to_list(A)}) + end, + tpl(Trace); +tpl([]) -> + ok. + +tracer({trace,_,call,{logger_disk_log_h,handle_cast,[Op|_]},Caller}, + {Pid,[{Mod,Func,Op}|Expected]}) -> + maybe_tracer_done(Pid,Expected,{Mod,Func,Op},Caller); +tracer({trace,_,call,{Mod=disk_log,Func=blog,[_,Data]},Caller}, {Pid,[{Mod,Func,Data}|Expected]}) -> + maybe_tracer_done(Pid,Expected,{Mod,Func,Data},Caller); +tracer({trace,_,call,{Mod,Func,_},Caller}, {Pid,[{Mod,Func}|Expected]}) -> + maybe_tracer_done(Pid,Expected,{Mod,Func},Caller); +tracer({trace,_,call,Call,Caller}, {Pid,Expected}) -> + ct:log("Tracer got unexpected: ~p~nCaller: ~p~nExpected: ~p~n",[Call,Caller,Expected]), + Pid ! {tracer_got_unexpected,Call,Expected}, + {Pid,Expected}. + +maybe_tracer_done(Pid,[],Got,Caller) -> + ct:log("Tracer got: ~p~nCaller: ~p~n",[Got,Caller]), + Pid ! tracer_done; +maybe_tracer_done(Pid,Expected,Got,Caller) -> + ct:log("Tracer got: ~p~nCaller: ~p~n",[Got,Caller]), + {Pid,Expected}. + +check_tracer(T) -> + receive + tracer_done -> + dbg:stop_clear(), + ok; + {tracer_got_unexpected,Got,Expected} -> + dbg:stop_clear(), + ct:fail({tracer_got_unexpected,Got,Expected}) + after T -> + dbg:stop_clear(), + ct:fail({timeout,tracer}) + end. + +escape([$+|Rest]) -> + [$\\,$+|escape(Rest)]; +escape([H|T]) -> + [H|escape(T)]; +escape([]) -> + []. + +h_proc_name() -> + h_proc_name(?MODULE). +h_proc_name(Name) -> + list_to_atom(lists:concat([logger_disk_log_h,"_",Name])). + +wait_for_process_up(T) -> + wait_for_process_up(?MODULE, h_proc_name(), T). + +wait_for_process_up(Name, RegName, T) -> + N = (T div 500) + 1, + wait_for_process_up1(Name, RegName, N). + +wait_for_process_up1(_Name, _RegName, 0) -> + error; +wait_for_process_up1(Name, RegName, N) -> + timer:sleep(500), + case whereis(RegName) of + Pid when is_pid(Pid) -> + case logger:get_handler_config(Name) of + {ok,_} -> + %% ct:pal("Process ~p up (~p tries left)",[Name,N]), + {ok,Pid}; + _ -> + wait_for_process_up1(Name, RegName, N-1) + end; + undefined -> + %% ct:pal("Waiting for process ~p (~p tries left)",[Name,N]), + wait_for_process_up1(Name, RegName, N-1) + end. + +file_delete(Log) -> + file:delete(Log). diff --git a/lib/kernel/test/logger_env_var_SUITE.erl b/lib/kernel/test/logger_env_var_SUITE.erl new file mode 100644 index 0000000000..e8d1a313dc --- /dev/null +++ b/lib/kernel/test/logger_env_var_SUITE.erl @@ -0,0 +1,683 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2018. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% +-module(logger_env_var_SUITE). + +-compile(export_all). + +-include_lib("kernel/include/logger.hrl"). +-include_lib("kernel/src/logger_internal.hrl"). + +-import(logger_test_lib,[setup/2,log/3,sync_and_read/3]). + +suite() -> + [{timetrap,{seconds,60}}, + {ct_hooks,[logger_test_lib]}]. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +groups() -> + [{error_logger,[],[error_logger_tty, + error_logger_tty_sasl_compatible, + error_logger_false, + error_logger_false_progress, + error_logger_false_sasl_compatible, + error_logger_silent, + error_logger_silent_sasl_compatible, + error_logger_file]}, + {logger,[],[logger_file, + logger_file_sasl_compatible, + logger_file_log_progress, + logger_file_no_filter, + logger_file_no_filter_level, + logger_file_formatter, + logger_filters, + logger_filters_stop, + logger_module_level, + logger_disk_log, + logger_disk_log_formatter, + logger_undefined, + logger_many_handlers_default_first, + logger_many_handlers_default_last, + logger_many_handlers_default_last_broken_filter + ]}, + {bad,[],[bad_error_logger, + bad_level, + bad_sasl_compatibility]}]. + +all() -> + [default, + default_sasl_compatible, + sasl_compatible_false, + sasl_compatible_false_no_progress, + sasl_compatible, + all_logger_level, + {group,bad}, + {group,error_logger}, + {group,logger} + ]. + +default(Config) -> + {ok,#{primary:=P,handlers:=Hs,module_levels:=ML},_Node} = setup(Config,[]), + notice = maps:get(level,P), + #{module:=logger_std_h} = StdC = find(?STANDARD_HANDLER,Hs), + all = maps:get(level,StdC), + StdFilters = maps:get(filters,StdC), + {domain,{_,{log,super,[otp,sasl]}}} = lists:keyfind(domain,1,StdFilters), + false = exists(simple,Hs), + false = exists(sasl,Hs), + [] = ML, + ok. + +default_sasl_compatible(Config) -> + {ok,#{primary:=P,handlers:=Hs,module_levels:=ML},_Node} = + setup(Config,[{logger_sasl_compatible,true}]), + info = maps:get(level,P), + #{module:=logger_std_h} = StdC = find(?STANDARD_HANDLER,Hs), + all = maps:get(level,StdC), + StdFilters = maps:get(filters,StdC), + {domain,{_,{log,super,[otp]}}} = lists:keyfind(domain,1,StdFilters), + false = exists(simple,Hs), + true = exists(sasl,Hs), + [] = ML, + ok. + +error_logger_tty(Config) -> + {ok,#{primary:=P,handlers:=Hs,module_levels:=ML},_Node} = + setup(Config,[{error_logger,tty}]), + notice = maps:get(level,P), + #{module:=logger_std_h} = StdC = find(?STANDARD_HANDLER,Hs), + all = maps:get(level,StdC), + StdFilters = maps:get(filters,StdC), + {domain,{_,{log,super,[otp,sasl]}}} = lists:keyfind(domain,1,StdFilters), + false = exists(simple,Hs), + false = exists(sasl,Hs), + [] = ML, + ok. + +error_logger_tty_sasl_compatible(Config) -> + {ok,#{primary:=P,handlers:=Hs,module_levels:=ML},_Node} = + setup(Config, + [{error_logger,tty}, + {logger_sasl_compatible,true}]), + info = maps:get(level,P), + #{module:=logger_std_h} = StdC = find(?STANDARD_HANDLER,Hs), + all = maps:get(level,StdC), + StdFilters = maps:get(filters,StdC), + {domain,{_,{log,super,[otp]}}} = lists:keyfind(domain,1,StdFilters), + false = exists(simple,Hs), + true = exists(sasl,Hs), + [] = ML, + ok. + +error_logger_false(Config) -> + {ok,#{handlers:=Hs,primary:=P,module_levels:=ML},_Node} = + setup(Config, + [{error_logger,false}, + {logger_level,notice}]), + false = exists(?STANDARD_HANDLER,Hs), + #{module:=logger_simple_h} = SimpleC = find(simple,Hs), + all = maps:get(level,SimpleC), + notice = maps:get(level,P), + SimpleFilters = maps:get(filters,SimpleC), + {domain,{_,{log,super,[otp,sasl]}}} = lists:keyfind(domain,1,SimpleFilters), + false = exists(sasl,Hs), + [] = ML, + ok. + +error_logger_false_progress(Config) -> + {ok,#{handlers:=Hs,primary:=P,module_levels:=ML},_Node} = + setup(Config, + [{error_logger,false}, + {logger_level,notice}]), + false = exists(?STANDARD_HANDLER,Hs), + #{module:=logger_simple_h} = SimpleC = find(simple,Hs), + all = maps:get(level,SimpleC), + notice = maps:get(level,P), + SimpleFilters = maps:get(filters,SimpleC), + {domain,{_,{log,super,[otp,sasl]}}} = lists:keyfind(domain,1,SimpleFilters), + false = exists(sasl,Hs), + [] = ML, + ok. + +error_logger_false_sasl_compatible(Config) -> + {ok,#{handlers:=Hs,primary:=P,module_levels:=ML},_Node} = + setup(Config, + [{error_logger,false}, + {logger_level,notice}, + {logger_sasl_compatible,true}]), + false = exists(?STANDARD_HANDLER,Hs), + #{module:=logger_simple_h} = SimpleC = find(simple,Hs), + all = maps:get(level,SimpleC), + info = maps:get(level,P), + SimpleFilters = maps:get(filters,SimpleC), + {domain,{_,{log,super,[otp]}}} = lists:keyfind(domain,1,SimpleFilters), + true = exists(sasl,Hs), + [] = ML, + ok. + +error_logger_silent(Config) -> + {ok,#{handlers:=Hs},_Node} = setup(Config, + [{error_logger,silent}]), + false = exists(?STANDARD_HANDLER,Hs), + false = exists(simple,Hs), + false = exists(sasl,Hs), + ok. + +error_logger_silent_sasl_compatible(Config) -> + {ok,#{handlers:=Hs},_Node} = setup(Config, + [{error_logger,silent}, + {logger_sasl_compatible,true}]), + false = exists(?STANDARD_HANDLER,Hs), + false = exists(simple,Hs), + true = exists(sasl,Hs), + ok. + + +error_logger_file(Config) -> + Log = file(Config,?FUNCTION_NAME), + {ok,_,Node} = setup(Config, + [{error_logger,{file,Log}}]), + check_default_log(Node,Log, + file,% dest + 0),% progress in std logger + ok. + + +logger_file(Config) -> + Log = file(Config,?FUNCTION_NAME), + {ok,#{primary:=P,handlers:=Hs,module_levels:=ML},Node} + = setup(Config, + [{logger, + [{handler,?STANDARD_HANDLER,logger_std_h, + #{config=>#{type=>{file,Log}}}}]}]), + check_default_log(Node,Log, + file,% dest + 0),% progress in std logger + + notice = maps:get(level,P), + #{module:=logger_std_h} = StdC = find(?STANDARD_HANDLER,Hs), + all = maps:get(level,StdC), + StdFilters = maps:get(filters,StdC), + {domain,{_,{log,super,[otp,sasl]}}} = lists:keyfind(domain,1,StdFilters), + false = exists(simple,Hs), + false = exists(sasl,Hs), + [] = ML, + ok. + +logger_file_sasl_compatible(Config) -> + Log = file(Config,?FUNCTION_NAME), + {ok,#{primary:=P,handlers:=Hs,module_levels:=ML},Node} + = setup(Config, + [{logger_sasl_compatible,true}, + {logger, + [{handler,?STANDARD_HANDLER,logger_std_h, + #{config=>#{type=>{file,Log}}}}]}]), + check_default_log(Node,Log, + file,% dest + 0),% progress in std logger + + info = maps:get(level,P), + #{module:=logger_std_h} = StdC = find(?STANDARD_HANDLER,Hs), + all = maps:get(level,StdC), + StdFilters = maps:get(filters,StdC), + {domain,{_,{log,super,[otp]}}} = lists:keyfind(domain,1,StdFilters), + false = exists(simple,Hs), + true = exists(sasl,Hs), + [] = ML, + ok. + +logger_file_log_progress(Config) -> + Log = file(Config,?FUNCTION_NAME), + {ok,#{primary:=P,handlers:=Hs,module_levels:=ML},Node} + = setup(Config, + [{logger_level,info}, + {logger, + [{handler,?STANDARD_HANDLER,logger_std_h, + #{config=>#{type=>{file,Log}}}}]}]), + check_default_log(Node,Log, + file,% dest + 6,% progress in std logger + info), + + info = maps:get(level,P), + #{module:=logger_std_h} = StdC = find(?STANDARD_HANDLER,Hs), + all = maps:get(level,StdC), + StdFilters = maps:get(filters,StdC), + {domain,{_,{log,super,[otp,sasl]}}} = lists:keyfind(domain,1,StdFilters), + false = exists(simple,Hs), + false = exists(sasl,Hs), + [] = ML, + ok. + +logger_file_no_filter(Config) -> + Log = file(Config,?FUNCTION_NAME), + {ok,#{handlers:=Hs},Node} + = setup(Config, + [{logger, + [{handler,?STANDARD_HANDLER,logger_std_h, + #{filter_default=>log,filters=>[], + config=>#{type=>{file,Log}}}}]}]), + check_default_log(Node,Log, + file,% dest + 6),% progress in std logger + + #{module:=logger_std_h} = StdC = find(?STANDARD_HANDLER,Hs), + all = maps:get(level,StdC), + [] = maps:get(filters,StdC), + false = exists(simple,Hs), + false = exists(sasl,Hs), + + ok. + +logger_file_no_filter_level(Config) -> + Log = file(Config,?FUNCTION_NAME), + {ok,#{handlers:=Hs},Node} + = setup(Config, + [{logger, + [{handler,?STANDARD_HANDLER,logger_std_h, + #{filters=>[],level=>error, + config=>#{type=>{file,Log}}}}]}]), + check_default_log(Node,Log, + file,% dest + 0,% progress in std logger + error),% level + + #{module:=logger_std_h} = StdC = find(?STANDARD_HANDLER,Hs), + error = maps:get(level,StdC), + [] = maps:get(filters,StdC), + false = exists(simple,Hs), + false = exists(sasl,Hs), + + ok. + +logger_file_formatter(Config) -> + Log = file(Config,?FUNCTION_NAME), + {ok,#{handlers:=Hs},Node} + = setup(Config, + [{logger, + [{handler,?STANDARD_HANDLER,logger_std_h, + #{filters=>[], + formatter=>{logger_formatter,#{}}, + config=>#{type=>{file,Log}}}}]}]), + check_single_log(Node,Log, + file,% dest + 6),% progress in std logger + + #{module:=logger_std_h} = StdC = find(?STANDARD_HANDLER,Hs), + all = maps:get(level,StdC), + [] = maps:get(filters,StdC), + false = exists(simple,Hs), + false = exists(sasl,Hs), + + ok. + +logger_filters(Config) -> + Log = file(Config,?FUNCTION_NAME), + {ok,#{handlers:=Hs,primary:=P},Node} + = setup(Config, + [{logger_level,info}, + {logger, + [{handler,?STANDARD_HANDLER,logger_std_h, + #{config=>#{type=>{file,Log}}}}, + {filters,log,[{stop_progress,{fun logger_filters:progress/2,stop}}]} + ]}]), + check_default_log(Node,Log, + file,% dest + 0,% progress in std logger + info), + + #{module:=logger_std_h} = StdC = find(?STANDARD_HANDLER,Hs), + all = maps:get(level,StdC), + StdFilters = maps:get(filters,StdC), + {domain,{_,{log,super,[otp,sasl]}}} = lists:keyfind(domain,1,StdFilters), + false = exists(simple,Hs), + false = exists(sasl,Hs), + LoggerFilters = maps:get(filters,P), + true = lists:keymember(stop_progress,1,LoggerFilters), + + ok. + +logger_filters_stop(Config) -> + Log = file(Config,?FUNCTION_NAME), + {ok,#{handlers:=Hs,primary:=P},Node} + = setup(Config, + [{logger_level,info}, + {logger, + [{handler,?STANDARD_HANDLER,logger_std_h, + #{filters=>[], + config=>#{type=>{file,Log}}}}, + {filters,stop,[{log_error,{fun logger_filters:level/2,{log,gt,info}}}]} + ]}]), + check_default_log(Node,Log, + file,% dest + 0,% progress in std logger + info), + + #{module:=logger_std_h} = StdC = find(?STANDARD_HANDLER,Hs), + all = maps:get(level,StdC), + [] = maps:get(filters,StdC), + false = exists(simple,Hs), + false = exists(sasl,Hs), + LoggerFilters = maps:get(filters,P), + true = lists:keymember(log_error,1,LoggerFilters), + + ok. + +logger_module_level(Config) -> + Log = file(Config,?FUNCTION_NAME), + {ok,#{handlers:=Hs,module_levels:=ModuleLevels},Node} + = setup(Config, + [{logger_level,info}, + {logger, + [{handler,?STANDARD_HANDLER,logger_std_h, + #{config=>#{type=>{file,Log}}}}, + {module_level,error,[supervisor]} + ]}]), + check_default_log(Node,Log, + file,% dest + 3,% progress in std logger + info), + + #{module:=logger_std_h} = StdC = find(?STANDARD_HANDLER,Hs), + all = maps:get(level,StdC), + StdFilters = maps:get(filters,StdC), + {domain,{_,{log,super,[otp,sasl]}}} = lists:keyfind(domain,1,StdFilters), + false = exists(simple,Hs), + false = exists(sasl,Hs), + [{supervisor,error}] = ModuleLevels, + ok. + +logger_disk_log(Config) -> + Log = file(Config,?FUNCTION_NAME), + {ok,#{handlers:=Hs},Node} + = setup(Config, + [{logger, + [{handler,?STANDARD_HANDLER,logger_disk_log_h, + #{config=>#{file=>Log}}}]}]), + check_default_log(Node,Log, + disk_log,% dest + 0),% progress in std logger + + #{module:=logger_disk_log_h} = StdC = find(?STANDARD_HANDLER,Hs), + all = maps:get(level,StdC), + StdFilters = maps:get(filters,StdC), + {domain,{_,{log,super,[otp,sasl]}}} = lists:keyfind(domain,1,StdFilters), + false = exists(simple,Hs), + false = exists(sasl,Hs), + + ok. + +logger_disk_log_formatter(Config) -> + Log = file(Config,?FUNCTION_NAME), + {ok,#{handlers:=Hs},Node} + = setup(Config, + [{logger, + [{handler,?STANDARD_HANDLER,logger_disk_log_h, + #{filters=>[], + formatter=>{logger_formatter,#{}}, + config=>#{file=>Log}}}]}]), + check_single_log(Node,Log, + disk_log,% dest + 6),% progress in std logger + + #{module:=logger_disk_log_h} = StdC = find(?STANDARD_HANDLER,Hs), + all = maps:get(level,StdC), + [] = maps:get(filters,StdC), + false = exists(simple,Hs), + false = exists(sasl,Hs), + + ok. + +logger_undefined(Config) -> + {ok,#{handlers:=Hs,primary:=P},_Node} = + setup(Config,[{logger,[{handler,?STANDARD_HANDLER,undefined}]}]), + false = exists(?STANDARD_HANDLER,Hs), + #{module:=logger_simple_h} = SimpleC = find(simple,Hs), + all = maps:get(level,SimpleC), + notice = maps:get(level,P), + SimpleFilters = maps:get(filters,SimpleC), + {domain,{_,{log,super,[otp,sasl]}}} = lists:keyfind(domain,1,SimpleFilters), + false = exists(sasl,Hs), + ok. + + +%% Test that we can add multiple handlers with the default first +logger_many_handlers_default_first(Config) -> + LogErr = file(Config,logger_many_handlers_default_first_error), + LogInfo = file(Config,logger_many_handlers_default_first_info), + + logger_many_handlers( + Config,[{logger, + [{handler,?STANDARD_HANDLER,logger_std_h, + #{level=>error, + filters=>[], + formatter=>{logger_formatter,#{}}, + config=>#{type=>{file,LogErr}}} + }, + {handler,info,logger_std_h, + #{level=>info, + filters=>[{level,{fun logger_filters:level/2,{stop,gteq,error}}}], + config=>#{type=>{file,LogInfo}}} + } + ]}, + {logger_level,info}], LogErr, LogInfo, 6). + +%% Test that we can add multiple handlers with the default last +logger_many_handlers_default_last(Config) -> + LogErr = file(Config,logger_many_handlers_default_last_error), + LogInfo = file(Config,logger_many_handlers_default_last_info), + logger_many_handlers( + Config,[{logger, + [{handler,info,logger_std_h, + #{level=>info, + filters=>[{level,{fun logger_filters:level/2,{stop,gteq,error}}}], + config=>#{type=>{file,LogInfo}}} + }, + {handler,?STANDARD_HANDLER,logger_std_h, + #{level=>error, + filters=>[], + formatter=>{logger_formatter,#{}}, + config=>#{type=>{file,LogErr}}} + } + ]}, + {logger_level,info}], LogErr, LogInfo, 7). + +%% Check that we can handle that an added logger has a broken filter +%% This used to cause a deadlock. +logger_many_handlers_default_last_broken_filter(Config) -> + LogErr = file(Config,logger_many_handlers_default_first_broken_filter_error), + LogInfo = file(Config,logger_many_handlers_default_first_broken_filter_info), + + logger_many_handlers( + Config,[{logger, + [{handler,info,logger_std_h, + #{level=>info, + filters=>[{broken,{fun logger_filters:level/2,broken_state}}, + {level,{fun logger_filters:level/2,{stop,gteq,error}}}], + config=>#{type=>{file,LogInfo}}} + }, + {handler,?STANDARD_HANDLER,logger_std_h, + #{level=>error, + filters=>[], + formatter=>{logger_formatter,#{}}, + config=>#{type=>{file,LogErr}}} + } + ]}, + {logger_level,info}], LogErr, LogInfo, 7). + +logger_many_handlers(Config, Env, LogErr, LogInfo, NumProgress) -> + {ok,_,Node} = setup(Config,Env), + check_single_log(Node,LogErr, + file,% dest + 0,% progress in std logger + error), % level + ok = rpc:call(Node,logger_std_h,filesync,[info]), + {ok, Bin} = file:read_file(LogInfo), + ct:log("Log content:~n~s",[Bin]), + match(Bin,<<"info:">>,NumProgress,info,info), + match(Bin,<<"notice:">>,1,notice,info), + match(Bin,<<"alert:">>,0,alert,info), + + ok. + +sasl_compatible_false(Config) -> + Log = file(Config,?FUNCTION_NAME), + {ok,_,Node} = setup(Config, + [{error_logger,{file,Log}}, + {logger_sasl_compatible,false}, + {logger_level,info}]), % to get progress + check_default_log(Node,Log, + file,% dest + 6,% progress in std logger + info), + ok. + +sasl_compatible_false_no_progress(Config) -> + Log = file(Config,?FUNCTION_NAME), + {ok,_,Node} = setup(Config, + [{error_logger,{file,Log}}, + {logger_sasl_compatible,false}]), + check_default_log(Node,Log, + file,% dest + 0),% progress in std logger + ok. + +sasl_compatible(Config) -> + Log = file(Config,?FUNCTION_NAME), + {ok,_,Node} = setup(Config, + [{error_logger,{file,Log}}, + {sasl_compatible,true}]), + check_default_log(Node,Log, + file,% dest + 0),% progress in std logger + ok. + +all_logger_level(Config) -> + [all_logger_level(Config,Level) || Level <- [none, + emergency, + alert, + critical, + error, + warning, + notice, + info, + debug, + all]], + ok. + +all_logger_level(Config,Level) -> + {ok,#{primary:=#{level:=Level}},Node} = setup(Config,[{logger_level,Level}]), + true = test_server:stop_node(Node), + ok. + +bad_error_logger(Config) -> + error = setup(Config,[{error_logger,baddest}]). + +bad_level(Config) -> + error = setup(Config,[{logger_level,badlevel}]). + +bad_sasl_compatibility(Config) -> + error = setup(Config,[{logger_sasl_compatible,badcomp}]). + +%%%----------------------------------------------------------------- +%%% Internal +file(Config,Func) -> + filename:join(proplists:get_value(priv_dir,Config), + lists:concat([Func,".log"])). + +check_default_log(Node,Log,Dest,NumProgress) -> + check_default_log(Node,Log,Dest,NumProgress,notice). +check_default_log(Node,Log,Dest,NumProgress,Level) -> + + {ok,Bin1,Bin2} = check_log(Node,Log,Dest), + + match(Bin1,<<"PROGRESS REPORT">>,NumProgress,info,Level), + match(Bin1,<<"ALERT REPORT">>,1,alert,Level), + match(Bin1,<<"INFO REPORT">>,0,notice,Level), + match(Bin1,<<"DEBUG REPORT">>,0,debug,Level), + + match(Bin2,<<"INFO REPORT">>,1,notice,Level), + match(Bin2,<<"DEBUG REPORT">>,0,debug,Level), + ok. + +check_single_log(Node,Log,Dest,NumProgress) -> + check_single_log(Node,Log,Dest,NumProgress,notice). +check_single_log(Node,Log,Dest,NumProgress,Level) -> + + {ok,Bin1,Bin2} = check_log(Node,Log,Dest), + + match(Bin1,<<"info:">>,NumProgress,info,Level), + match(Bin1,<<"alert:">>,1,alert,Level), + match(Bin1,<<"debug:">>,0,debug,Level), + + match(Bin2,<<"info:">>,NumProgress+1,info,Level), + match(Bin2,<<"debug:">>,0,debug,Level), + + ok. + +check_log(Node,Log,Dest) -> + + ok = log(Node,alert,["dummy1"]), + ok = log(Node,debug,["dummy1"]), + + %% Check that there are progress reports (supervisor and + %% application_controller) and an error report (the call above) in + %% the log. There should not be any info reports yet. + {ok,Bin1} = sync_and_read(Node,Dest,Log), + ct:log("Log content:~n~s",[Bin1]), + + %% Then stop sasl and see that the info report from + %% application_controller is there + ok = rpc:call(Node,application,stop,[sasl]), + {ok,Bin2} = sync_and_read(Node,Dest,Log), + ct:log("Log content:~n~s",[Bin2]), + {ok,Bin1,Bin2}. + +match(Bin,Pattern,0,_,_) -> + nomatch = re:run(Bin,Pattern,[{capture,none}]); +match(Bin,Pattern,N,LogLevel,ConfLevel) -> + case logger:compare_levels(LogLevel,ConfLevel) of + lt -> match(Bin,Pattern,0,LogLevel,ConfLevel); + _ -> + {match,M} = re:run(Bin,Pattern,[{capture,all},global]), + N = length(M) + end. + +find(Id,Handlers) -> + case lists:search(fun(#{id:=Id0}) when Id0=:=Id-> true; + (_) -> false end, + Handlers) of + {value,Config} -> + Config; + false -> + false + end. + +exists(Id,Handlers) -> + case find(Id,Handlers) of + false -> + false; + _ -> + true + end. diff --git a/lib/kernel/test/logger_filters_SUITE.erl b/lib/kernel/test/logger_filters_SUITE.erl new file mode 100644 index 0000000000..11cce8fd20 --- /dev/null +++ b/lib/kernel/test/logger_filters_SUITE.erl @@ -0,0 +1,227 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2018. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% +-module(logger_filters_SUITE). + +-compile(export_all). + +-include_lib("common_test/include/ct.hrl"). +-include_lib("kernel/include/logger.hrl"). + +-define(ndlog, + #{level=>info,msg=>{"Line: ~p",[?LINE]},meta=>#{}}). +-define(dlog(Domain), + #{level=>info,msg=>{"Line: ~p",[?LINE]},meta=>#{domain=>Domain}}). +-define(llog(Level), + #{level=>Level,msg=>{"Line: ~p",[?LINE]},meta=>#{}}). +-define(plog, + #{level=>info, + msg=>{report,#{label=>{?MODULE,progress}}}, + meta=>#{line=>?LINE}}). +-define(rlog(Node), + #{level=>info, + msg=>{"Line: ~p",[?LINE]}, + meta=>#{gl=>rpc:call(Node,erlang,whereis,[user])}}). + +-define(TRY(X), my_try(fun() -> X end)). + +suite() -> + [{timetrap,{seconds,30}}]. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_group(_Group, Config) -> + Config. + +end_per_group(_Group, _Config) -> + ok. + +init_per_testcase(_TestCase, Config) -> + Config. + +end_per_testcase(Case, Config) -> + try apply(?MODULE,Case,[cleanup,Config]) + catch error:undef -> ok + end, + ok. + +groups() -> + []. + +all() -> + [domain, + level, + progress, + remote_gl]. + +domain(_Config) -> + L1 = logger_filters:domain(L1=?dlog([]),{log,super,[]}), + stop = logger_filters:domain(?dlog([]),{stop,super,[]}), + L2 = logger_filters:domain(L2=?dlog([]),{log,sub,[]}), + stop = logger_filters:domain(?dlog([]),{stop,sub,[]}), + L3 = logger_filters:domain(L3=?dlog([]),{log,equal,[]}), + stop = logger_filters:domain(?dlog([]),{stop,equal,[]}), + ignore = logger_filters:domain(?dlog([]),{log,not_equal,[]}), + ignore = logger_filters:domain(?dlog([]),{stop,not_equal,[]}), + ignore = logger_filters:domain(?dlog([]),{log,undefined,[]}), + ignore = logger_filters:domain(?dlog([]),{stop,undefined,[]}), + + L4 = logger_filters:domain(L4=?dlog([a]),{log,super,[a,b]}), + stop = logger_filters:domain(?dlog([a]),{stop,super,[a,b]}), + ignore = logger_filters:domain(?dlog([a]),{log,sub,[a,b]}), + ignore = logger_filters:domain(?dlog([a]),{stop,sub,[a,b]}), + ignore = logger_filters:domain(?dlog([a]),{log,equal,[a,b]}), + ignore = logger_filters:domain(?dlog([a]),{stop,equal,[a,b]}), + L5 = logger_filters:domain(L5=?dlog([a]),{log,not_equal,[a,b]}), + stop = logger_filters:domain(?dlog([a]),{stop,not_equal,[a,b]}), + ignore = logger_filters:domain(?dlog([a]),{log,undefined,[a,b]}), + ignore = logger_filters:domain(?dlog([a]),{stop,undefined,[a,b]}), + + ignore = logger_filters:domain(?dlog([a,b]),{log,super,[a]}), + ignore = logger_filters:domain(?dlog([a,b]),{stop,super,[a]}), + L6 = logger_filters:domain(L6=?dlog([a,b]),{log,sub,[a]}), + stop = logger_filters:domain(?dlog([a,b]),{stop,sub,[a]}), + ignore = logger_filters:domain(?dlog([a,b]),{log,equal,[a]}), + ignore = logger_filters:domain(?dlog([a,b]),{stop,equal,[a]}), + L7 = logger_filters:domain(L7=?dlog([a,b]),{log,not_equal,[a]}), + stop = logger_filters:domain(?dlog([a,b]),{stop,not_equal,[a]}), + ignore = logger_filters:domain(?dlog([a,b]),{log,undefined,[a]}), + ignore = logger_filters:domain(?dlog([a,b]),{stop,undefined,[a]}), + + ignore = logger_filters:domain(?ndlog,{log,super,[a]}), + ignore = logger_filters:domain(?ndlog,{stop,super,[a]}), + ignore = logger_filters:domain(?ndlog,{log,sub,[a]}), + ignore = logger_filters:domain(?ndlog,{stop,sub,[a]}), + ignore = logger_filters:domain(?ndlog,{log,equal,[a]}), + ignore = logger_filters:domain(?ndlog,{stop,equal,[a]}), + L8 = logger_filters:domain(L8=?ndlog,{log,not_equal,[a]}), + stop = logger_filters:domain(?ndlog,{stop,not_equal,[a]}), + L9 = logger_filters:domain(L9=?ndlog,{log,undefined,[a]}), + stop = logger_filters:domain(?ndlog,{stop,undefined,[a]}), + + L10 = logger_filters:domain(L10=?dlog([a,b,c,d]),{log,super,[a,b,c,d]}), + stop = logger_filters:domain(?dlog([a,b,c,d]),{stop,super,[a,b,c,d]}), + L11 = logger_filters:domain(L11=?dlog([a,b,c,d]),{log,sub,[a,b,c,d]}), + stop = logger_filters:domain(?dlog([a,b,c,d]),{stop,sub,[a,b,c,d]}), + L12 = logger_filters:domain(L12=?dlog([a,b,c,d]),{log,equal,[a,b,c,d]}), + stop = logger_filters:domain(?dlog([a,b,c,d]),{stop,equal,[a,b,c,d]}), + ignore = logger_filters:domain(?dlog([a,b,c,d]),{log,not_equal,[a,b,c,d]}), + ignore = logger_filters:domain(?dlog([a,b,c,d]),{stop,not_equal,[a,b,c,d]}), + ignore = logger_filters:domain(?dlog([a,b,c,d]),{log,undefined,[a,b,c,d]}), + ignore = logger_filters:domain(?dlog([a,b,c,d]),{stop,undefined,[a,b,c,d]}), + + %% A domain field in meta which is not a list is allowed by the + %% filter, but since MatchDomain is always a list of atoms, only + %% Action=not_equal can ever match. + ignore = logger_filters:domain(?dlog(dummy),{log,super,[a,b,c,d]}), + ignore = logger_filters:domain(?dlog(dummy),{stop,super,[a,b,c,d]}), + ignore = logger_filters:domain(?dlog(dummy),{log,sub,[a,b,c,d]}), + ignore = logger_filters:domain(?dlog(dummy),{stop,sub,[a,b,c,d]}), + ignore = logger_filters:domain(?dlog(dummy),{log,equal,[a,b,c,d]}), + ignore = logger_filters:domain(?dlog(dummy),{stop,equal,[a,b,c,d]}), + L13 = logger_filters:domain(L13=?dlog(dummy),{log,not_equal,[a,b,c,d]}), + stop = logger_filters:domain(?dlog(dummy),{stop,not_equal,[a,b,c,d]}), + ignore = logger_filters:domain(?dlog(dummy),{log,undefined,[a,b,c,d]}), + ignore = logger_filters:domain(?dlog(dummy),{stop,undefined,[a,b,c,d]}), + + {error,badarg} = ?TRY(logger_filters:domain(?ndlog,bad)), + {error,badarg} = ?TRY(logger_filters:domain(?ndlog,{bad,super,[]})), + {error,badarg} = ?TRY(logger_filters:domain(?ndlog,{log,bad,[]})), + {error,badarg} = ?TRY(logger_filters:domain(?ndlog,{log,super,bad})), + + ok. + +level(_Config) -> + ignore = logger_filters:level(?llog(info),{log,lt,info}), + ignore = logger_filters:level(?llog(info),{stop,lt,info}), + ignore = logger_filters:level(?llog(info),{log,gt,info}), + ignore = logger_filters:level(?llog(info),{stop,gt,info}), + L1 = logger_filters:level(L1=?llog(info),{log,lteq,info}), + stop = logger_filters:level(?llog(info),{stop,lteq,info}), + L2 = logger_filters:level(L2=?llog(info),{log,gteq,info}), + stop = logger_filters:level(?llog(info),{stop,gteq,info}), + L3 = logger_filters:level(L3=?llog(info),{log,eq,info}), + stop = logger_filters:level(?llog(info),{stop,eq,info}), + ignore = logger_filters:level(?llog(info),{log,neq,info}), + ignore = logger_filters:level(?llog(info),{stop,neq,info}), + + ignore = logger_filters:level(?llog(error),{log,lt,info}), + ignore = logger_filters:level(?llog(error),{stop,lt,info}), + L4 = logger_filters:level(L4=?llog(error),{log,gt,info}), + stop = logger_filters:level(?llog(error),{stop,gt,info}), + ignore = logger_filters:level(?llog(error),{log,lteq,info}), + ignore = logger_filters:level(?llog(error),{stop,lteq,info}), + L5 = logger_filters:level(L5=?llog(error),{log,gteq,info}), + stop = logger_filters:level(?llog(error),{stop,gteq,info}), + ignore = logger_filters:level(?llog(error),{log,eq,info}), + ignore = logger_filters:level(?llog(error),{stop,eq,info}), + L6 = logger_filters:level(L6=?llog(error),{log,neq,info}), + stop = logger_filters:level(?llog(error),{stop,neq,info}), + + L7 = logger_filters:level(L7=?llog(info),{log,lt,error}), + stop = logger_filters:level(?llog(info),{stop,lt,error}), + ignore = logger_filters:level(?llog(info),{log,gt,error}), + ignore = logger_filters:level(?llog(info),{stop,gt,error}), + L8 = logger_filters:level(L8=?llog(info),{log,lteq,error}), + stop = logger_filters:level(?llog(info),{stop,lteq,error}), + ignore = logger_filters:level(?llog(info),{log,gteq,error}), + ignore = logger_filters:level(?llog(info),{stop,gteq,error}), + ignore = logger_filters:level(?llog(info),{log,eq,error}), + ignore = logger_filters:level(?llog(info),{stop,eq,error}), + L9 = logger_filters:level(L9=?llog(info),{log,neq,error}), + stop = logger_filters:level(?llog(info),{stop,neq,error}), + + {error,badarg} = ?TRY(logger_filters:level(?llog(info),bad)), + {error,badarg} = ?TRY(logger_filters:level(?llog(info),{bad,eq,info})), + {error,badarg} = ?TRY(logger_filters:level(?llog(info),{log,bad,info})), + {error,badarg} = ?TRY(logger_filters:level(?llog(info),{log,eq,bad})), + + ok. + +progress(_Config) -> + L1 = logger_filters:progress(L1=?plog,log), + stop = logger_filters:progress(?plog,stop), + ignore = logger_filters:progress(?ndlog,log), + ignore = logger_filters:progress(?ndlog,stop), + + {error,badarg} = ?TRY(logger_filters:progress(?plog,bad)), + + ok. + +remote_gl(_Config) -> + {ok,Node} = test_server:start_node(?FUNCTION_NAME,slave,[]), + L1 = logger_filters:remote_gl(L1=?rlog(Node),log), + stop = logger_filters:remote_gl(?rlog(Node),stop), + ignore = logger_filters:remote_gl(?ndlog,log), + ignore = logger_filters:remote_gl(?ndlog,stop), + + {error,badarg} = ?TRY(logger_filters:remote_gl(?rlog(Node),bad)), + ok. + +remote_gl(cleanup,_Config) -> + [test_server:stop_node(N) || N<-nodes()]. + +%%%----------------------------------------------------------------- +%%% Called by macro ?TRY(X) +my_try(Fun) -> + try Fun() catch C:R -> {C,R} end. diff --git a/lib/kernel/test/logger_formatter_SUITE.erl b/lib/kernel/test/logger_formatter_SUITE.erl new file mode 100644 index 0000000000..8c13f0f908 --- /dev/null +++ b/lib/kernel/test/logger_formatter_SUITE.erl @@ -0,0 +1,886 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2018. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% +-module(logger_formatter_SUITE). + +-compile(export_all). + +-include_lib("common_test/include/ct.hrl"). +-include_lib("kernel/include/logger.hrl"). + +-define(TRY(X), my_try(fun() -> X end)). + +suite() -> + [{timetrap,{seconds,30}}]. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_group(_Group, Config) -> + Config. + +end_per_group(_Group, _Config) -> + ok. + +init_per_testcase(_TestCase, Config) -> + Config. + +end_per_testcase(Case, Config) -> + try apply(?MODULE,Case,[cleanup,Config]) + catch error:undef -> ok + end, + ok. + +groups() -> + []. + +all() -> + [default, + legacy_header, + error_logger_notice_header, + single_line, + template, + format_msg, + report_cb, + max_size, + depth, + chars_limit, + format_mfa, + format_time, + level_or_msg_in_meta, + faulty_log, + faulty_config, + faulty_msg, + check_config, + update_config]. + +default(_Config) -> + String1 = format(info,{"~p",[term]},#{},#{}), + ct:log(String1), + [_DateTime,"info:","term\n"] = string:lexemes(String1," "), + + Time = timestamp(), + ExpectedTimestamp = default_time_format(Time), + String2 = format(info,{"~p",[term]},#{time=>Time},#{}), + ct:log(String2), + " info: term\n" = string:prefix(String2,ExpectedTimestamp), + ok. + +legacy_header(_Config) -> + Time = timestamp(), + String1 = format(info,{"~p",[term]},#{time=>Time},#{legacy_header=>true, + single_line=>false}), + ct:log(String1), + "=INFO REPORT==== "++Rest = String1, + [Timestamp,"\nterm\n"] = string:lexemes(Rest," ="), + [D,M,Y,H,Min,S,Micro] = string:lexemes(Timestamp,"-:."), + integer(D,31), + integer(Y,2018,infinity), + integer(H,23), + integer(Min,59), + integer(S,59), + integer(Micro,999999), + true = lists:member(M,["Jan","Feb","Mar","Apr","May","Jun", + "Jul","Aug","Sep","Oct","Nov","Dec"]), + + String2 = format(info,{"~p",[term]},#{time=>Time},#{legacy_header=>false, + single_line=>false}), + ct:log(String2), + ExpectedTimestamp = default_time_format(Time), + " info:\nterm\n" = string:prefix(String2,ExpectedTimestamp), + + String3 = format(info,{"~p",[term]},#{time=>Time},#{legacy_header=>bad, + single_line=>false}), + ct:log(String3), + String3 = String2, + + String4 = format(info,{"~p",[term]},#{time=>Time}, + #{legacy_header=>true, + single_line=>true}), % <---ignored + ct:log(String4), + String4 = String1, + + String5 = format(info,{"~p",[term]},#{}, % <--- no time + #{legacy_header=>true, + single_line=>false}), + ct:log(String5), + "=INFO REPORT==== "++_ = String5, + ok. + +error_logger_notice_header(_Config) -> + Meta1 = #{error_logger=>#{tag => info_report,type => std_info}}, + String1 = format(notice,{"~p",[term]},Meta1, + #{legacy_header=>true, + error_logger_notice_header=>notice}), + ct:log(String1), + "=NOTICE REPORT==== "++_ = String1, + + String2 = format(notice,{"~p",[term]},Meta1, + #{legacy_header=>true, + error_logger_notice_header=>info}), + ct:log(String2), + "=INFO REPORT==== "++_ = String2, + + String3 = format(notice,{"~p",[term]},#{}, + #{legacy_header=>true, + error_logger_notice_header=>notice}), + ct:log(String3), + "=NOTICE REPORT==== "++_ = String3, + + String4 = format(notice,{"~p",[term]},#{}, + #{legacy_header=>true, + error_logger_notice_header=>info}), + ct:log(String4), + "=NOTICE REPORT==== "++_ = String4, + + ok. + +single_line(_Config) -> + Time = timestamp(), + ExpectedTimestamp = default_time_format(Time), + String1 = format(info,{"~p",[term]},#{time=>Time},#{single_line=>true}), + ct:log(String1), + " info: term\n" = string:prefix(String1,ExpectedTimestamp), + + String2 = format(info,{"~p",[term]},#{time=>Time},#{single_line=>false}), + ct:log(String2), + " info:\nterm\n" = string:prefix(String2,ExpectedTimestamp), + + String2 = format(info,{"~p",[term]},#{time=>Time},#{single_line=>bad}), + + + %% Test that no extra commas/spaces are added when removing + %% newlines, especially not after "=>" in a map association (as + %% was the case in OTP-21.0, when the only single_line adjustment + %% was done by regexp replacement of "\n" by ", "). + Prefix = + "Some characters to fill the line ------------------------------------- ", + String3 = format(info,{"~s~p~n~s~p~n",[Prefix, + lists:seq(1,10), + Prefix, + #{a=>map,with=>a,few=>accociations}]}, + #{time=>Time}, + #{single_line=>true}), + ct:log(String3), + match = re:run(String3,"\\[1,2,3,4,5,6,7,8,9,10\\]",[{capture,none}]), + match = re:run(String3, + "#{a => map,few => accociations,with => a}", + [{capture,none}]), + + %% This part is added to make sure that the previous test made + %% sense, i.e. that there would actually be newlines inside the + %% list and map. + String4 = format(info,{"~s~p~n~s~p~n",[Prefix, + lists:seq(1,10), + Prefix, + #{a=>map,with=>a,few=>accociations}]}, + #{time=>Time}, + #{single_line=>false}), + ct:log(String4), + match = re:run(String4,"\\[1,2,3,\n",[global,{capture,none}]), + {match,Match4} = re:run(String4,"=>\n",[global,{capture,all}]), + 3 = length(Match4), + + %% Test that big metadata fields do not get line breaks + String5 = format(info,"", + #{mymeta=>lists:seq(1,100)}, + #{single_line=>true,template=>[mymeta,"\n"]}), + ct:log(String5), + [_] = string:lexemes(String5,"\n"), + + %% Ensure that the previous test made sense, i.e. that the + %% metadata field does produce multiple lines if + %% single_line==false. + String6 = format(info,"", + #{mymeta=>lists:seq(1,100)}, + #{single_line=>false,template=>[mymeta,"\n"]}), + ct:log(String6), + [_,_|_] = string:lexemes(String6,"\n"), + + ok. + +template(_Config) -> + Time = timestamp(), + + Template1 = [msg], + String1 = format(info,{"~p",[term]},#{time=>Time},#{template=>Template1}), + ct:log(String1), + "term" = String1, + + Template2 = [msg,unknown], + String2 = format(info,{"~p",[term]},#{time=>Time},#{template=>Template2}), + ct:log(String2), + "term" = String2, + + Template3 = ["string"], + String3 = format(info,{"~p",[term]},#{time=>Time},#{template=>Template3}), + ct:log(String3), + "string" = String3, + + Template4 = ["string\nnewline"], + String4 = format(info,{"~p",[term]},#{time=>Time},#{template=>Template4, + single_line=>true}), + ct:log(String4), + "string\nnewline" = String4, + + Template5 = [], + String5 = format(info,{"~p",[term]},#{time=>Time},#{template=>Template5}), + ct:log(String5), + "" = String5, + + Ref6 = erlang:make_ref(), + Meta6 = #{atom=>some_atom, + integer=>632, + list=>[list,"string",4321,#{},{tuple}], + mfa=>{mod,func,0}, + pid=>self(), + ref=>Ref6, + string=>"some string", + time=>Time, + tuple=>{1,atom,"list"}, + nested=>#{subkey=>subvalue}}, + Template6 = lists:join(";",lists:sort(maps:keys(maps:remove(nested,Meta6))) ++ + [[nested,subkey]]), + String6 = format(info,{"~p",[term]},Meta6,#{template=>Template6, + single_line=>true}), + ct:log(String6), + SelfStr = pid_to_list(self()), + RefStr6 = ref_to_list(Ref6), + ListStr = "[list,\"string\",4321,#{},{tuple}]", + ExpectedTime6 = default_time_format(Time), + ["some_atom", + "632", + ListStr, + "mod:func/0", + SelfStr, + RefStr6, + "some string", + ExpectedTime6, + "{1,atom,\"list\"}", + "subvalue"] = string:lexemes(String6,";"), + + Meta7 = #{time=>Time, + nested=>#{key1=>#{subkey1=>value1}, + key2=>value2}}, + Template7 = lists:join(";",[nested, + [nested,key1], + [nested,key1,subkey1], + [nested,key2], + [nested,key2,subkey2], + [nested,key3], + [nested,key3,subkey3]]), + String7 = format(info,{"~p",[term]},Meta7,#{template=>Template7, + single_line=>true}), + ct:log(String7), + [MultipleKeysStr7, + "#{subkey1 => value1}", + "value1", + "value2", + "", + "", + ""] = string:split(String7,";",all), + %% Order of keys is not fixed + case MultipleKeysStr7 of + "#{key2 => value2,key1 => #{subkey1 => value1}}" -> ok; + "#{key1 => #{subkey1 => value1},key2 => value2}" -> ok; + _ -> ct:fail({full_nested_map_unexpected,MultipleKeysStr7}) + end, + + Meta8 = #{time=>Time, + nested=>#{key1=>#{subkey1=>value1}, + key2=>value2}}, + Template8 = + lists:join( + ";", + [{nested,["exist:",nested],["noexist"]}, + {[nested,key1],["exist:",[nested,key1]],["noexist"]}, + {[nested,key1,subkey1],["exist:",[nested,key1,subkey1]],["noexist"]}, + {[nested,key2],["exist:",[nested,key2]],["noexist"]}, + {[nested,key2,subkey2],["exist:",[nested,key2,subkey2]],["noexist"]}, + {[nested,key3],["exist:",[nested,key3]],["noexist"]}, + {[nested,key3,subkey3],["exist:",[nested,key3,subkey3]],["noexist"]}]), + String8 = format(info,{"~p",[term]},Meta8,#{template=>Template8, + single_line=>true}), + ct:log(String8), + [MultipleKeysStr8, + "exist:#{subkey1 => value1}", + "exist:value1", + "exist:value2", + "noexist", + "noexist", + "noexist"] = string:split(String8,";",all), + %% Order of keys is not fixed + case MultipleKeysStr8 of + "exist:#{key2 => value2,key1 => #{subkey1 => value1}}" -> ok; + "exist:#{key1 => #{subkey1 => value1},key2 => value2}" -> ok; + _ -> ct:fail({full_nested_map_unexpected,MultipleKeysStr8}) + end, + + ok. + +format_msg(_Config) -> + Template = [msg], + + String1 = format(info,{"~p",[term]},#{},#{template=>Template}), + ct:log(String1), + "term" = String1, + + String2 = format(info,{"list",[term]},#{},#{template=>Template}), + ct:log(String2), + "FORMAT ERROR: \"list\" - [term]" = String2, + + String3 = format(info,{report,term},#{},#{template=>Template}), + ct:log(String3), + "term" = String3, + + String4 = format(info,{report,term}, + #{report_cb=>fun(_)-> {"formatted",[]} end}, + #{template=>Template}), + ct:log(String4), + "formatted" = String4, + + String5 = format(info,{report,term}, + #{report_cb=>fun(_)-> faulty_return end}, + #{template=>Template}), + ct:log(String5), + "REPORT_CB/1 ERROR: term; Returned: faulty_return" = String5, + + String6 = format(info,{report,term}, + #{report_cb=>fun(_)-> erlang:error(fun_crashed) end}, + #{template=>Template}), + ct:log(String6), + "REPORT_CB/1 CRASH: term; Reason: {error,fun_crashed,"++_ = String6, + + String7 = format(info,{report,term}, + #{report_cb=>fun(_,_)-> ['not',a,string] end}, + #{template=>Template}), + ct:log(String7), + "REPORT_CB/2 ERROR: term; Returned: ['not',a,string]" = String7, + + String8 = format(info,{report,term}, + #{report_cb=>fun(_,_)-> faulty_return end}, + #{template=>Template}), + ct:log(String8), + "REPORT_CB/2 ERROR: term; Returned: faulty_return" = String8, + + String9 = format(info,{report,term}, + #{report_cb=>fun(_,_)-> erlang:error(fun_crashed) end}, + #{template=>Template}), + ct:log(String9), + "REPORT_CB/2 CRASH: term; Reason: {error,fun_crashed,"++_ = String9, + + %% strings are not formatted + String10 = format(info,{string,"string"}, + #{report_cb=>fun(_)-> {"formatted",[]} end}, + #{template=>Template}), + ct:log(String10), + "string" = String10, + + String11 = format(info,{string,['not',printable,list]}, + #{report_cb=>fun(_)-> {"formatted",[]} end}, + #{template=>Template}), + ct:log("~ts",[String11]), % avoiding ct_log crash + "FORMAT ERROR: \"~ts\" - [['not',printable,list]]" = String11, + + String12 = format(info,{string,"string"},#{},#{template=>Template}), + ct:log(String12), + "string" = String12, + + ok. + +report_cb(_Config) -> + Template = [msg], + MetaFun = fun(_) -> {"meta_rcb",[]} end, + ConfigFun = fun(_) -> {"config_rcb",[]} end, + "term" = format(info,{report,term},#{},#{template=>Template}), + "meta_rcb" = + format(info,{report,term},#{report_cb=>MetaFun},#{template=>Template}), + "config_rcb" = + format(info,{report,term},#{},#{template=>Template, + report_cb=>ConfigFun}), + "config_rcb" = + format(info,{report,term},#{report_cb=>MetaFun},#{template=>Template, + report_cb=>ConfigFun}), + ok. + +max_size(_Config) -> + Cfg = #{template=>[msg], + single_line=>false}, + "12345678901234567890" = + format(info,{"12345678901234567890",[]},#{},Cfg), + %% application:set_env(kernel,logger_max_size,11), + %% "12345678901234567890" = % min value is 50, so this is not limited + %% format(info,{"12345678901234567890",[]},#{},Cfg), + %% "12345678901234567890123456789012345678901234567..." = % 50 + %% format(info, + %% {"123456789012345678901234567890123456789012345678901234567890", + %% []}, + %% #{}, + %% Cfg), + %% application:set_env(kernel,logger_max_size,53), + %% "12345678901234567890123456789012345678901234567890..." = %53 + %% format(info, + %% {"123456789012345678901234567890123456789012345678901234567890", + %% []}, + %% #{}, + %% Cfg), + "123456789012..." = + format(info,{"12345678901234567890",[]},#{},Cfg#{max_size=>15}), + "12345678901234567890" = + format(info,{"12345678901234567890",[]},#{},Cfg#{max_size=>unlimited}), + %% Check that one newline at the end of the line is kept (if it exists) + "12345678901...\n" = + format(info,{"12345678901234567890\n",[]},#{},Cfg#{max_size=>15}), + "12345678901...\n" = + format(info,{"12345678901234567890",[]},#{},Cfg#{template=>[msg,"\n"], + max_size=>15}), + ok. +max_size(cleanup,_Config) -> + application:unset_env(kernel,logger_max_size), + ok. + +depth(_Config) -> + Template = [msg], + "[1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0]" = + format(info, + {"~p",[[1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0]]}, + #{}, + #{template=>Template}), + application:set_env(kernel,error_logger_format_depth,11), + "[1,2,3,4,5,6,7,8,9,0|...]" = + format(info, + {"~p",[[1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0]]}, + #{}, + #{template=>Template}), + "[1,2,3,4,5,6,7,8,9,0,1,2|...]" = + format(info, + {"~p",[[1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0]]}, + #{}, + #{template=>Template, + depth=>13}), + "[1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0]" = + format(info, + {"~p",[[1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0]]}, + #{}, + #{template=>Template, + depth=>unlimited}), + ok. +depth(cleanup,_Config) -> + application:unset_env(kernel,error_logger_format_depth), + ok. + +chars_limit(_Config) -> + FA = {"LoL: ~p~nL: ~p~nMap: ~p~n", + [lists:duplicate(10,lists:seq(1,100)), + lists:seq(1,100), + maps:from_list(lists:zip(lists:seq(1,100), + lists:duplicate(100,value)))]}, + Meta = #{time=>timestamp()}, + Template = [time," - ", msg, "\n"], + FC = #{template=>Template, + depth=>unlimited, + max_size=>unlimited, + chars_limit=>unlimited, + single_line=>true}, + CL1 = 80, + String1 = format(info,FA,Meta,FC#{chars_limit=>CL1}), + L1 = string:length(String1), + ct:log("String1: ~p~nLength1: ~p~n",[lists:flatten(String1),L1]), + true = L1 > CL1, + true = L1 < CL1 + 15, + + String2 = format(info,FA,Meta,FC#{chars_limit=>CL1,depth=>10}), + L2 = string:length(String2), + ct:log("String2: ~p~nLength2: ~p~n",[lists:flatten(String2),L2]), + String2 = String1, + + CL3 = 200, + String3 = format(info,FA,Meta,FC#{chars_limit=>CL3}), + L3 = string:length(String3), + ct:log("String3: ~p~nLength3: ~p~n",[lists:flatten(String3),L3]), + true = L3 > CL3, + true = L3 < CL3 + 15, + + String4 = format(info,FA,Meta,FC#{chars_limit=>CL3,depth=>10}), + L4 = string:length(String4), + ct:log("String4: ~p~nLength4: ~p~n",[lists:flatten(String4),L4]), + true = L4 > CL3, + true = L4 < CL3 + 15, + + %% Test that max_size truncates the string which is limited by + %% depth and chars_limit + MS5 = 150, + String5 = format(info,FA,Meta,FC#{chars_limit=>CL3,depth=>10,max_size=>MS5}), + L5 = string:length(String5), + ct:log("String5: ~p~nLength5: ~p~n",[String5,L5]), + L5 = MS5, + true = lists:prefix(lists:sublist(String5,L5-4),String4), + + %% Test that chars_limit limits string also + Str = "123456789012345678901234567890123456789012345678901234567890123456789", + CL6 = 80, + String6 = format(info,{string,Str},Meta,FC#{chars_limit=>CL6}), + L6 = string:length(String6), + ct:log("String6: ~p~nLength6: ~p~n",[String6,L6]), + L6 = CL6, + + ok. + +format_mfa(_Config) -> + Template = [mfa], + + Meta1 = #{mfa=>{mod,func,0}}, + String1 = format(info,{"~p",[term]},Meta1,#{template=>Template}), + ct:log(String1), + "mod:func/0" = String1, + + Meta2 = #{mfa=>{mod,func,[]}}, + String2 = format(info,{"~p",[term]},Meta2,#{template=>Template}), + ct:log(String2), + "mod:func/0" = String2, + + Meta3 = #{mfa=>"mod:func/0"}, + String3 = format(info,{"~p",[term]},Meta3,#{template=>Template}), + ct:log(String3), + "mod:func/0" = String3, + + Meta4 = #{mfa=>othermfa}, + String4 = format(info,{"~p",[term]},Meta4,#{template=>Template}), + ct:log(String4), + "othermfa" = String4, + + ok. + +format_time(_Config) -> + Time = timestamp(), + Meta = #{time=>Time}, + FC = #{template=>[time]}, + Msg = {string,""}, + ExpectedLocal = default_time_format(Time,false), + ExpectedUtc = default_time_format(Time,true), + + %% default - local time + ExpectedLocal = format(info,Msg,Meta,FC), + + %% time_offset config parameter to formatter + ExpectedLocal = format(info,Msg,Meta,FC#{time_offset=>""}), + ExpectedUtc = format(info,Msg,Meta,FC#{time_offset=>"Z"}), + + %% stdlib utc_log works when time_offset parameter is not set + application:set_env(stdlib,utc_log,true), + ExpectedUtc = format(info,Msg,Meta,FC), + + %% sasl utc_log overwrites stdlib utc_log + application:set_env(sasl,utc_log,false), + ExpectedLocal = format(info,Msg,Meta,FC), + + %% sasl utc_log overwrites stdlib utc_log + application:set_env(sasl,utc_log,true), + application:set_env(stdlib,utc_log,false), + ExpectedUtc = format(info,Msg,Meta,FC), + + %% time_offset config parameter to formatter + %% overwrites sasl and stdlib utc_log + application:set_env(sasl,utc_log,false), + ExpectedUtc = format(info,Msg,Meta,FC#{time_offset=>"Z"}), + + %% time_offset config parameter to formatter + %% overwrites sasl and stdlib utc_log + application:set_env(sasl,utc_log,true), + application:set_env(stdlib,utc_log,true), + ExpectedLocal = format(info,Msg,Meta,FC#{time_offset=>""}), + + %% time_designator config parameter to formatter + ExpectedLocalS = default_time_format(Time,false,$\s), + ExpectedUtcS = default_time_format(Time,true,$\s), + + ExpectedLocalS = format(info,Msg,Meta,FC#{time_offset=>"", + time_designator=>$\s}), + ExpectedUtcS = format(info,Msg,Meta,FC#{time_offset=>"Z", + time_designator=>$\s}), + + ok. + +format_time(cleanup,_Config) -> + application:unset_env(sasl,utc_log), + application:unset_env(stdlib,utc_log), + ok. + +level_or_msg_in_meta(_Config) -> + %% The template contains atoms to pick out values from meta, + %% or level/msg to add these from the log event. What if you have + %% a key named 'level' or 'msg' in meta and want to display + %% its value? + %% For now we simply ignore Meta on this and display the + %% actual level and msg from the log event. + + Meta = #{level=>mylevel, + msg=>"metamsg"}, + Template = [level,";",msg], + String = format(info,{"~p",[term]},Meta,#{template=>Template}), + ct:log(String), + "info;term" = String, % so mylevel and "metamsg" are ignored + + ok. + +faulty_log(_Config) -> + %% Unexpected log (should be type logger:log_event()) - print error + {error, + function_clause, + {logger_formatter,format,[_,_],_}} = + ?TRY(logger_formatter:format(unexp_log,#{})), + ok. + +faulty_config(_Config) -> + {error, + function_clause, + {logger_formatter,format,[_,_],_}} = + ?TRY(logger_formatter:format(#{level=>info, + msg=>{"~p",[term]}, + meta=>#{time=>timestamp()}}, + unexp_config)), + ok. + +faulty_msg(_Config) -> + {error, + function_clause, + {logger_formatter,_,_,_}} = + ?TRY(logger_formatter:format(#{level=>info, + msg=>term, + meta=>#{time=>timestamp()}}, + #{})), + ok. + +-define(cfgerr(X), {error,{invalid_formatter_config,logger_formatter,X}}). +check_config(_Config) -> + ok = logger_formatter:check_config(#{}), + ?cfgerr(bad) = logger_formatter:check_config(bad), + + C1 = #{chars_limit => 1, + depth => 1, + legacy_header => true, + error_logger_notice_header => info, + max_size => 1, + report_cb => fun(R) -> {"~p",[R]} end, + single_line => false, + template => [], + time_designator => $T, + time_offset => 0}, + ok = logger_formatter:check_config(C1), + + ok = logger_formatter:check_config(#{chars_limit => unlimited}), + ?cfgerr({chars_limit,bad}) = + logger_formatter:check_config(#{chars_limit => bad}), + + ok = logger_formatter:check_config(#{depth => unlimited}), + ?cfgerr({depth,bad}) = + logger_formatter:check_config(#{depth => bad}), + + ok = logger_formatter:check_config(#{legacy_header => false}), + ?cfgerr({legacy_header,bad}) = + logger_formatter:check_config(#{legacy_header => bad}), + + ok = logger_formatter:check_config(#{error_logger_notice_header => notice}), + ?cfgerr({error_logger_notice_header,bad}) = + logger_formatter:check_config(#{error_logger_notice_header => bad}), + + ok = logger_formatter:check_config(#{max_size => unlimited}), + ?cfgerr({max_size,bad}) = + logger_formatter:check_config(#{max_size => bad}), + + ok = + logger_formatter:check_config(#{report_cb => fun(_,_) -> "" end}), + ?cfgerr({report_cb,F}) = + logger_formatter:check_config(#{report_cb => F=fun(_,_,_) -> {"",[]} end}), + ?cfgerr({report_cb,bad}) = + logger_formatter:check_config(#{report_cb => bad}), + + ok = logger_formatter:check_config(#{single_line => true}), + ?cfgerr({single_line,bad}) = + logger_formatter:check_config(#{single_line => bad}), + + Ts = [[key], + [[key1,key2]], + [{key,[key],[]}], + [{[key1,key2],[[key1,key2]],["noexist"]}], + ["string"]], + [begin + ct:log("check template: ~p",[T]), + ok = logger_formatter:check_config(#{template => T}) + end + || T <- Ts], + + ETs = [bad, + [{key,bad}], + [{key,[key],bad}], + [{key,[key],"bad"}], + "bad", + [[key,$a,$b,$c]], + [[$a,$b,$c,key]]], + [begin + ct:log("check template: ~p",[T]), + {error,{invalid_formatter_template,logger_formatter,T}} = + logger_formatter:check_config(#{template => T}) + end + || T <- ETs], + + ?cfgerr({time_designator,bad}) = + logger_formatter:check_config(#{time_designator => bad}), + ?cfgerr({time_designator,"b"}) = + logger_formatter:check_config(#{time_designator => "b"}), + + ok = logger_formatter:check_config(#{time_offset => -1}), + ok = logger_formatter:check_config(#{time_offset => "+02:00"}), + ok = logger_formatter:check_config(#{time_offset => "-23:59"}), + ok = logger_formatter:check_config(#{time_offset => "+24:00"}), + ok = logger_formatter:check_config(#{time_offset => "-25:00"}), + ?cfgerr({time_offset,bad}) = + logger_formatter:check_config(#{time_offset => bad}), + ?cfgerr({time_offset,"02:00"}) = + logger_formatter:check_config(#{time_offset => "02:00"}), + ?cfgerr({time_offset,"+02"}) = + logger_formatter:check_config(#{time_offset => "+02"}), + + ok. + +%% Test that formatter config can be changed, and that the default +%% template is updated accordingly +update_config(_Config) -> + {error,{not_found,?MODULE}} = logger:update_formatter_config(?MODULE,#{}), + + logger:add_handler_filter(default,silence,{fun(_,_) -> stop end,ok}), + ok = logger:add_handler(?MODULE,?MODULE,#{}), + D = lists:seq(1,1000), + logger:notice("~p~n",[D]), + {Lines1,C1} = check_log(), + [ct:log(L) || L <- Lines1], + ct:log("~p",[C1]), + [Line1] = Lines1, + [_Time,"notice: "++D1] = string:split(Line1," "), + true = length(D1)>3000, + true = #{}==C1, + + ok = logger:update_formatter_config(?MODULE,single_line,false), + logger:notice("~p~n",[D]), + {Lines2,C2} = check_log(), + [ct:log(L) || L <- Lines2], + ct:log("~p",[C2]), + true = length(Lines2)>50, + true = #{single_line=>false}==C2, + + ok = logger:update_formatter_config(?MODULE,#{legacy_header=>true}), + logger:notice("~p~n",[D]), + {Lines3,C3} = check_log(), + [ct:log(L) || L <- Lines3], + ct:log("~p",[C3]), + ["=NOTICE REPORT==== "++_|D3] = Lines3, + true = length(D3)>50, + true = #{legacy_header=>true,single_line=>false}==C3, + + ok = logger:update_formatter_config(?MODULE,single_line,true), + logger:notice("~p~n",[D]), + {Lines4,C4} = check_log(), + [ct:log(L) || L <- Lines4], + ct:log("~p",[C4]), + ["=NOTICE REPORT==== "++_,D4] = Lines4, + true = length(D4)>3000, + true = #{legacy_header=>true,single_line=>true}==C4, + + %% Finally, check that error_logger_notice_header works, default=info + error_logger:info_msg("~p",[D]), + {Lines5,C5} = check_log(), + [ct:log(L) || L <- Lines5], + ct:log("~p",[C5]), + ["=INFO REPORT==== "++_,_D5] = Lines5, + + ok=logger:update_formatter_config(?MODULE,error_logger_notice_header,notice), + error_logger:info_msg("~p",[D]), + {Lines6,C6} = check_log(), + [ct:log(L) || L <- Lines6], + ct:log("~p",[C6]), + ["=NOTICE REPORT==== "++_,_D6] = Lines6, + + {error,{invalid_formatter_config,bad}} = + logger:update_formatter_config(?MODULE,bad), + {error,{invalid_formatter_config,logger_formatter,{depth,bad}}} = + logger:update_formatter_config(?MODULE,depth,bad), + + ok. + +update_config(cleanup,_Config) -> + _ = logger:remove_handler(?MODULE), + _ = logger:remove_handler_filter(default,silence), + ok. + +%%%----------------------------------------------------------------- +%%% Internal +format(Level,Msg,Meta,Config) -> + format(#{level=>Level,msg=>Msg,meta=>add_time(Meta)},Config). + +format(Log,Config) -> + lists:flatten(logger_formatter:format(Log,Config)). + +default_time_format(Timestamp) -> + default_time_format(Timestamp,false). + +default_time_format(Timestamp,Utc) -> + default_time_format(Timestamp,Utc,$T). + +default_time_format(Timestamp,Utc,Sep) -> + Offset = if Utc -> "Z"; + true -> "" + end, + calendar:system_time_to_rfc3339(Timestamp,[{unit,microsecond}, + {time_designator,Sep}, + {offset,Offset}]). + +integer(Str) -> + is_integer(list_to_integer(Str)). +integer(Str,Max) -> + integer(Str,0,Max). +integer(Str,Min,Max) -> + Int = list_to_integer(Str), + Int >= Min andalso Int =<Max. + +%%%----------------------------------------------------------------- +%%% Called by macro ?TRY(X) +my_try(Fun) -> + try Fun() catch C:R:S -> {C,R,hd(S)} end. + +timestamp() -> + erlang:system_time(microsecond). + +%% necessary? +add_time(#{time:=_}=Meta) -> + Meta; +add_time(Meta) -> + Meta#{time=>timestamp()}. + +%%%----------------------------------------------------------------- +%%% handler callback +log(Log,#{formatter:={M,C}}) -> + put(log,{M:format(Log,C),C}), + ok. + +check_log() -> + {S,C} = erase(log), + {string:lexemes(S,"\n"),C}. diff --git a/lib/kernel/test/logger_legacy_SUITE.erl b/lib/kernel/test/logger_legacy_SUITE.erl new file mode 100644 index 0000000000..c3cab07d81 --- /dev/null +++ b/lib/kernel/test/logger_legacy_SUITE.erl @@ -0,0 +1,288 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2018. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% +-module(logger_legacy_SUITE). + +-compile(export_all). +-compile({nowarn_deprecated_function,[{gen_fsm,start,3}, + {gen_fsm,send_all_state_event,2}]}). + +-include_lib("common_test/include/ct.hrl"). +-include_lib("kernel/include/logger.hrl"). +-include_lib("kernel/src/logger_internal.hrl"). + +%%%----------------------------------------------------------------- +%%% This test suite test that log events from within OTP can be +%%% delivered to legacy error_logger event handlers on the same format +%%% as before 'logger' was introduced. +%%% +%%% Before changing the expected format of any of the log events in +%%% this suite, please make sure that the backwards incompatibility it +%%% introduces is ok. +%%% ----------------------------------------------------------------- + +-define(check(Expected), + receive Expected -> + [] = test_server:messages_get() + after 1000 -> + ct:fail({report_not_received, + {line,?LINE}, + {got,test_server:messages_get()}}) + end). +-define(check_no_flush(Expected), + receive Expected -> + ok + after 1000 -> + ct:fail({report_not_received, + {line,?LINE}, + {got,test_server:messages_get()}}) + end). + +suite() -> + [{timetrap,{seconds,30}}]. + +init_per_suite(Config) -> + logger:add_handler(error_logger,error_logger, + #{level=>info,filter_default=>stop}), + Config. + +end_per_suite(_Config) -> + logger:remove_handler(error_logger), + ok. + +init_per_group(std, Config) -> + ok = logger:set_handler_config( + error_logger,filters, + [{domain,{fun logger_filters:domain/2,{log,super,[otp]}}}]), + Config; +init_per_group(sasl, Config) -> + %% Since default level is notice, and progress reports are info, + %% we need to raise the global logger level to info in order to + %% receive these. + ok = logger:set_primary_config(level,info), + ok = logger:set_handler_config( + error_logger,filters, + [{domain,{fun logger_filters:domain/2,{log,super,[otp,sasl]}}}]), + + %% cth_log_redirect checks if sasl is started before displaying + %% any sasl reports - so just to see the real sasl reports in tc + %% log: + {ok,Apps} = application:ensure_all_started(sasl), + [{stop_apps,Apps}|Config]; +init_per_group(_Group, Config) -> + Config. + +end_per_group(sasl, Config) -> + Apps = ?config(stop_apps,Config), + [application:stop(App) || App <- Apps], + ok = logger:set_primary_config(level,notice), + ok; +end_per_group(_Group, _Config) -> + ok. + +init_per_testcase(_TestCase, Config) -> + error_logger:add_report_handler(?MODULE,{event_handler,self()}), + Config. + +end_per_testcase(Case, Config) -> + %% Using gen_event directly here, instead of + %% error_logger:delete_report_handler. This is to avoid + %% automatically stopping the error_logger process due to removing + %% the last handler. + gen_event:delete_handler(error_logger,?MODULE,[]), + try apply(?MODULE,Case,[cleanup,Config]) + catch error:undef -> ok + end, + ok. + +groups() -> + [{std,[],[gen_server, + gen_event, + gen_fsm, + gen_statem]}, + {sasl,[],[sasl_reports, + supervisor_handle_info]}]. + +all() -> + [{group,std}, + {group,sasl}]. + +gen_server(_Config) -> + {ok,Pid} = gen_server:start(?MODULE,gen_server,[]), + Msg = fun() -> erlang:error({badmatch,b}) end, + Pid ! Msg, + ?check({warning_msg,"** Undefined handle_info in ~p"++_,[?MODULE,Msg]}), + ok = gen_server:cast(Pid,Msg), + ?check({error,"** Generic server ~tp terminating"++_, + [Pid,{'$gen_cast',Msg},gen_server,{{badmatch,b},_}]}). + +gen_event(_Config) -> + {ok,Pid} = gen_event:start(), + ok = gen_event:add_handler(Pid,?MODULE,gen_event), + Msg = fun() -> erlang:error({badmatch,b}) end, + Pid ! Msg, + ?check({warning_msg,"** Undefined handle_info in ~tp"++_,[?MODULE,Msg]}), + gen_event:notify(Pid,Msg), + ?check({error,"** gen_event handler ~p crashed."++_, + [?MODULE,Pid,Msg,gen_event,{{badmatch,b},_}]}). + +gen_fsm(_Config) -> + {ok,Pid} = gen_fsm:start(?MODULE,gen_fsm,[]), + Msg = fun() -> erlang:error({badmatch,b}) end, + Pid ! Msg, + ?check({warning_msg,"** Undefined handle_info in ~p"++_,[?MODULE,Msg]}), + gen_fsm:send_all_state_event(Pid,Msg), + ?check({error,"** State machine ~tp terminating"++_, + [Pid,Msg,mystate,gen_fsm,{{badmatch,b},_}]}). + +gen_statem(_Config) -> + {ok,Pid} = gen_statem:start(?MODULE,gen_statem,[]), + Msg = fun() -> erlang:error({badmatch,b}) end, + Pid ! Msg, + ?check({error,"** State machine ~tp terminating"++_, + [Pid,{info,Msg},{mystate,gen_statem},error,{badmatch,b}|_]}). + +sasl_reports(Config) -> + App = {application,?MODULE,[{description, ""}, + {vsn, "1.0"}, + {modules, [?MODULE]}, + {registered, []}, + {applications, []}, + {mod, {?MODULE, []}}]}, + AppStr = io_lib:format("~p.",[App]), + Dir = ?config(priv_dir,Config), + AppFile = filename:join(Dir,?MODULE_STRING++".app"), + ok = file:write_file(AppFile,AppStr), + true = code:add_patha(Dir), + ok = application:start(?MODULE), + SupName = sup_name(), + Pid = whereis(SupName), + [{ch,ChPid,_,_}] = supervisor:which_children(Pid), + Node = node(), + ?check_no_flush({info_report,progress,[{application,?MODULE}, + {started_at,Node}]}), + ?check({info_report,progress,[{supervisor,{local,SupName}}, + {started,[{pid,ChPid}|_]}]}), + ok = gen_server:cast(ChPid, fun() -> + spawn_link(fun() -> receive x->ok end end) + end), + Msg = fun() -> erlang:error({badmatch,b}) end, + ok = gen_server:cast(ChPid,Msg), + ?check_no_flush({error,"** Generic server ~tp terminating"++_, + [ChPid,{'$gen_cast',Msg},gen_server,{{badmatch,b},_}]}), + ?check_no_flush({error_report,crash_report, + [[{initial_call,_}, + {pid,ChPid}, + {registered_name,[]}, + {error_info,{error,{badmatch,b},_}}, + {ancestors,_}, + {message_queue_len,_}, + {messages,_}, + {links,[Pid,Neighbour]}, + {dictionary,_}, + {trap_exit,_}, + {status,_}, + {heap_size,_}, + {stack_size,_}, + {reductions,_}], + [{neighbour,[{pid,Neighbour}, + {registered_name,_}, + {initial_call,_}, + {current_function,_}, + {ancestors,_}, + {message_queue_len,_}, + {links,[ChPid]}, + {trap_exit,_}, + {status,_}, + {heap_size,_}, + {stack_size,_}, + {reductions,_}, + {current_stacktrace,_}]}]]}), + ?check_no_flush({error_report,supervisor_report, + [{supervisor,{local,SupName}}, + {errorContext,child_terminated}, + {reason,{{badmatch,b},_}}, + {offender,[{pid,ChPid}|_]}]}), + ?check({info_report,progress,[{supervisor,{local,SupName}}, + {started,_}]}), + + ok = application:stop(?MODULE), + ?check({info_report,std_info,[{application,?MODULE}, + {exited,stopped}, + {type,temporary}]}). + +sasl_reports(cleanup,_Config) -> + application:stop(?MODULE). + +supervisor_handle_info(_Config) -> + {ok,Pid} = supervisor:start_link({local,sup_name()},?MODULE,supervisor), + ?check({info_report,progress,[{supervisor,_},{started,_}]}), + Pid ! msg, + ?check({error,"Supervisor received unexpected message: ~tp~n",[msg]}). + +supervisor_handle_info(cleanup,_Config) -> + Pid = whereis(sup_name()), + unlink(Pid), + exit(Pid,shutdown). + +%%%----------------------------------------------------------------- +%%% Callbacks for error_logger event handler, gen_server, gen_statem, +%%% gen_fsm, gen_event, supervisor and application. +start(_,_) -> + supervisor:start_link({local,sup_name()},?MODULE,supervisor). + +init(supervisor) -> + {ok,{#{},[#{id=>ch,start=>{gen_server,start_link,[?MODULE,gen_server,[]]}}]}}; +init(StateMachine) when StateMachine==gen_statem; StateMachine==gen_fsm -> + {ok,mystate,StateMachine}; +init(State) -> + {ok,State}. + +%% error_logger event handler +handle_event({Tag,_Gl,{_Pid,Type,Report}},{_,Pid}=State) -> + Pid ! {Tag,Type,Report}, + {ok,State}; +%% other gen_event +handle_event(Fun,State) when is_function(Fun) -> + Fun(), + {next_state,State}. + +%% gen_fsm +handle_event(Fun,State,Data) when is_function(Fun) -> + Fun(), + {next_state,State,Data}. + +%% gen_statem +handle_event(info,Fun,State,Data) when is_function(Fun) -> + Fun(), + {next_state,State,Data}. + +%% gen_server +handle_cast(Fun,State) when is_function(Fun) -> + Fun(), + {noreply,State}. + +%% gen_statem +callback_mode() -> + handle_event_function. + +%%%----------------------------------------------------------------- +%%% Internal +sup_name() -> + list_to_atom(?MODULE_STRING++"_sup"). diff --git a/lib/kernel/test/logger_simple_h_SUITE.erl b/lib/kernel/test/logger_simple_h_SUITE.erl new file mode 100644 index 0000000000..e0ad792bdb --- /dev/null +++ b/lib/kernel/test/logger_simple_h_SUITE.erl @@ -0,0 +1,209 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2018. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% +-module(logger_simple_h_SUITE). + +-compile(export_all). + +-include_lib("common_test/include/ct.hrl"). +-include_lib("kernel/include/logger.hrl"). +-include_lib("kernel/src/logger_internal.hrl"). + +-import(logger_test_lib, [setup/2, log/3, sync_and_read/3]). + +-define(check_no_log,[] = test_server:messages_get()). +-define(check(Expected), + receive {log,Expected} -> + [] = test_server:messages_get() + after 1000 -> + ct:fail({report_not_received, + {line,?LINE}, + {expected,Expected}, + {got,test_server:messages_get()}}) + end). + +-define(str,"Log from "++atom_to_list(?FUNCTION_NAME)++ + ":"++integer_to_list(?LINE)). +-define(map_rep,#{function=>?FUNCTION_NAME, line=>?LINE}). +-define(keyval_rep,[{function,?FUNCTION_NAME}, {line,?LINE}]). + +suite() -> + [{timetrap,{seconds,30}}, + {ct_hooks, [logger_test_lib]}]. + +init_per_suite(Config) -> + Hs0 = logger:get_handler_config(), + Hs = lists:keydelete(cth_log_redirect,1,Hs0), + [ok = logger:remove_handler(Id) || {Id,_,_} <- Hs], + Env = [{App,Key,application:get_env(App,Key)} || + {App,Key} <- [{kernel,logger_level}]], + [{env,Env},{logger,Hs}|Config]. + +end_per_suite(Config) -> + [application:set_env(App,Key,Val) || {App,Key,Val} <- ?config(env,Config)], + Hs = ?config(logger,Config), + [ok = logger:add_handler(Id,Mod,C) || {Id,Mod,C} <- Hs], + ok. + +init_per_group(_Group, Config) -> + Config. + +end_per_group(_Group, _Config) -> + ok. + +init_per_testcase(_TestCase, Config) -> + Config. + +end_per_testcase(Case, Config) -> + try apply(?MODULE,Case,[cleanup,Config]) + catch error:undef -> ok + end, + ok. + +groups() -> + []. + +all() -> + [start_stop, + replace_default, + replace_file, + replace_disk_log + ]. + +start_stop(_Config) -> + undefined = whereis(logger_simple_h), + register(logger_simple_h,self()), + {error,_} = logger:add_handler(simple, + logger_simple_h, + #{filter_default=>log}), + unregister(logger_simple_h), + ok = logger:add_handler(simple,logger_simple_h,#{filter_default=>log}), + Pid = whereis(logger_simple_h), + true = is_pid(Pid), + ok = logger:remove_handler(simple), + false = is_pid(whereis(logger_simple_h)), + ok. +start_stop(cleanup,_Config) -> + logger:remove_handler(simple). + +%% This testcase just tests that it does not crash, the default handler prints +%% to stdout which we cannot read from in a detached slave. +replace_default(Config) -> + + {ok, _, Node} = logger_test_lib:setup(Config, [{logger, [{handler, default, undefined}]}]), + log(Node, emergency, [?str]), + log(Node, alert, [?str,[]]), + log(Node, error, [?map_rep]), + log(Node, info, [?keyval_rep]), + log(Node, info, [?keyval_rep++[not_key_val]]), + rpc:call(Node, error_logger, error_report, [some_type,?map_rep]), + rpc:call(Node, error_logger, warning_report, ["some_type",?map_rep]), + log(Node, critical, [?str,[?keyval_rep]]), + log(Node, notice, [["fake",string,"line:",?LINE]]), + + ok = rpc:call(Node, logger, add_handlers, [kernel]), + + ok. + +replace_file(Config) -> + + {ok, _, Node} = logger_test_lib:setup(Config, [{logger, [{handler, default, undefined}]}]), + log(Node, emergency, [M1=?str]), + log(Node, alert, [M2=?str,[]]), + log(Node, error, [?map_rep]), + log(Node, warning, [?keyval_rep]), + log(Node, warning, [?keyval_rep++[not_key_val]]), + log(Node, critical, [?str,[?keyval_rep]]), + log(Node, notice, [["fake",string,"line:",?LINE]]), + + File = filename:join(proplists:get_value(priv_dir,Config), + atom_to_list(?FUNCTION_NAME)++".log"), + + ok = rpc:call(Node, logger, add_handlers, + [[{handler, default, logger_std_h, + #{ config => #{ type => {file, File} }, + formatter => {?DEFAULT_FORMATTER,?DEFAULT_FORMAT_CONFIG}}}]]), + + {ok,Bin} = sync_and_read(Node, file, File), + Lines = [unicode:characters_to_list(L) || + L <- binary:split(Bin,<<"\n">>,[global,trim])], + ["=EMERGENCY REPORT===="++_, + M1, + "=ALERT REPORT===="++_, + M2, + "=ERROR REPORT===="++_, + _, + _, + "=WARNING REPORT===="++_, + _, + _, + "=WARNING REPORT===="++_, + _, + _, + _, + "=CRITICAL REPORT===="++_, + _, + _, + "=NOTICE REPORT===="++_, + _ + ] = Lines, + ok. + +replace_disk_log(Config) -> + + {ok, _, Node} = logger_test_lib:setup(Config, [{logger, [{handler, default, undefined}]}]), + log(Node, emergency, [M1=?str]), + log(Node, alert, [M2=?str,[]]), + log(Node, error, [?map_rep]), + log(Node, warning, [?keyval_rep]), + log(Node, warning, [?keyval_rep++[not_key_val]]), + log(Node, critical, [?str,[?keyval_rep]]), + log(Node, notice, [["fake",string,"line:",?LINE]]), + + File = filename:join(proplists:get_value(priv_dir,Config), + atom_to_list(?FUNCTION_NAME)++".log"), + + ok = rpc:call(Node, logger, add_handlers, + [[{handler, default, logger_disk_log_h, + #{ config => #{ file => File }, + formatter => {?DEFAULT_FORMATTER,?DEFAULT_FORMAT_CONFIG}}}]]), + {ok,Bin} = sync_and_read(Node, disk_log, File), + Lines = [unicode:characters_to_list(L) || + L <- binary:split(Bin,<<"\n">>,[global,trim])], + ["=EMERGENCY REPORT===="++_, + M1, + "=ALERT REPORT===="++_, + M2, + "=ERROR REPORT===="++_, + _, + _, + "=WARNING REPORT===="++_, + _, + _, + "=WARNING REPORT===="++_, + _, + _, + _, + "=CRITICAL REPORT===="++_, + _, + _, + "=NOTICE REPORT===="++_, + _ + ] = Lines, + ok. diff --git a/lib/kernel/test/logger_std_h_SUITE.erl b/lib/kernel/test/logger_std_h_SUITE.erl new file mode 100644 index 0000000000..3426567bbf --- /dev/null +++ b/lib/kernel/test/logger_std_h_SUITE.erl @@ -0,0 +1,1607 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2018. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% +-module(logger_std_h_SUITE). + +-compile(export_all). + +-include_lib("common_test/include/ct.hrl"). +-include_lib("kernel/include/logger.hrl"). +-include_lib("kernel/src/logger_internal.hrl"). +-include_lib("kernel/src/logger_h_common.hrl"). +-include_lib("stdlib/include/ms_transform.hrl"). +-include_lib("kernel/include/file.hrl"). + +-define(check_no_log, [] = test_server:messages_get()). +-define(check(Expected), + receive + {log,Expected} -> + [] = test_server:messages_get() + after 5000 -> + ct:fail({report_not_received, + {line,?LINE}, + {expected,Expected}, + {got,test_server:messages_get()}}) + end). + +-define(msg,"Log from "++atom_to_list(?FUNCTION_NAME)++ + ":"++integer_to_list(?LINE)). +-define(bin(Msg), list_to_binary(Msg++"\n")). +-define(domain,#{domain=>[?MODULE]}). + +suite() -> + [{timetrap,{seconds,30}}, + {ct_hooks,[logger_test_lib]}]. + +init_per_suite(Config) -> + timer:start(), % to avoid progress report + {ok,#{formatter:=OrigFormatter}} = + logger:get_handler_config(?STANDARD_HANDLER), + [{formatter,OrigFormatter}|Config]. + +end_per_suite(Config) -> + {OrigMod,OrigConf} = proplists:get_value(formatter,Config), + logger:set_handler_config(?STANDARD_HANDLER,formatter,{OrigMod,OrigConf}), + ok. + +init_per_group(_Group, Config) -> + Config. + +end_per_group(_Group, _Config) -> + ok. + +init_per_testcase(TestHooksCase, Config) when + TestHooksCase == write_failure; + TestHooksCase == sync_failure -> + case (fun() -> ?TEST_HOOKS_TAB == undefined end)() of + true -> + {skip,"Define the TEST_HOOKS macro to run this test"}; + false -> + ct:print("********** ~w **********", [TestHooksCase]), + Config + end; +init_per_testcase(OPCase, Config) when + OPCase == qlen_kill_new; + OPCase == restart_after -> + case re:run(erlang:system_info(system_version), + "dirty-schedulers-TEST", + [{capture,none}]) of + match -> + {skip,"Overload protection test skipped on dirty-schedulers-TEST"}; + nomatch -> + ct:print("********** ~w **********", [OPCase]), + Config + end; +init_per_testcase(TestCase, Config) -> + ct:print("********** ~w **********", [TestCase]), + Config. + +end_per_testcase(Case, Config) -> + try apply(?MODULE,Case,[cleanup,Config]) + catch error:undef -> ok + end, + ok. + +groups() -> + []. + +all() -> + [add_remove_instance_tty, + add_remove_instance_standard_io, + add_remove_instance_standard_error, + add_remove_instance_file1, + add_remove_instance_file2, + default_formatter, + errors, + formatter_fail, + config_fail, + crash_std_h_to_file, + crash_std_h_to_disk_log, + bad_input, + info_and_reset, + reconfig, + file_opts, + sync, + write_failure, + sync_failure, + op_switch_to_sync_file, + op_switch_to_sync_tty, + op_switch_to_drop_file, + op_switch_to_drop_tty, + op_switch_to_flush_file, + op_switch_to_flush_tty, + limit_burst_disabled, + limit_burst_enabled_one, + limit_burst_enabled_period, + kill_disabled, + qlen_kill_new, + qlen_kill_std, + mem_kill_new, + mem_kill_std, + restart_after, + handler_requests_under_load + ]. + +add_remove_instance_tty(_Config) -> + {error,{handler_not_added,{invalid_config,logger_std_h,{type,tty}}}} = + logger:add_handler(?MODULE,logger_std_h, + #{config => #{type => tty}, + filter_default=>log, + formatter=>{?MODULE,self()}}), + ok. + +add_remove_instance_standard_io(_Config) -> + add_remove_instance_nofile(standard_io). +add_remove_instance_standard_io(cleanup,_Config) -> + logger_std_h_remove(). + +add_remove_instance_standard_error(_Config) -> + add_remove_instance_nofile(standard_error). +add_remove_instance_standard_error(cleanup,_Config) -> + logger_std_h_remove(). + +add_remove_instance_file1(Config) -> + Dir = ?config(priv_dir,Config), + Log = filename:join(Dir,"stdlog1.txt"), + Type = {file,Log}, + add_remove_instance_file(Log, Type). +add_remove_instance_file1(cleanup,_Config) -> + logger_std_h_remove(). + +add_remove_instance_file2(Config) -> + Dir = ?config(priv_dir,Config), + Log = filename:join(Dir,"stdlog2.txt"), + Type = {file,Log,[raw,append]}, + add_remove_instance_file(Log, Type). +add_remove_instance_file2(cleanup,_Config) -> + logger_std_h_remove(). + +add_remove_instance_file(Log, Type) -> + ok = logger:add_handler(?MODULE, + logger_std_h, + #{config => #{type => Type}, + filter_default=>stop, + filters=>?DEFAULT_HANDLER_FILTERS([?MODULE]), + formatter=>{?MODULE,self()}}), + Pid = whereis(h_proc_name()), + true = is_pid(Pid), + logger:notice(M1=?msg,?domain), + ?check(M1), + B1 = ?bin(M1), + try_read_file(Log, {ok,B1}, filesync_rep_int()), + ok = logger:remove_handler(?MODULE), + timer:sleep(500), + undefined = whereis(h_proc_name()), + logger:notice(?msg,?domain), + ?check_no_log, + try_read_file(Log, {ok,B1}, filesync_rep_int()), + ok. + +default_formatter(_Config) -> + ok = logger:set_handler_config(?STANDARD_HANDLER,formatter, + {?DEFAULT_FORMATTER,?DEFAULT_FORMAT_CONFIG}), + ct:capture_start(), + logger:notice(M1=?msg), + timer:sleep(100), + ct:capture_stop(), + [Msg] = ct:capture_get(), + match = re:run(Msg,"=NOTICE REPORT====.*\n"++M1,[{capture,none}]), + ok. + +errors(Config) -> + Dir = ?config(priv_dir,Config), + Log = filename:join(Dir,?FUNCTION_NAME), + + ok = logger:add_handler(?MODULE,logger_std_h,#{}), + {error,{already_exist,?MODULE}} = + logger:add_handler(?MODULE,logger_std_h,#{}), + + {error,{not_found,no_such_name}} = logger:remove_handler(no_such_name), + + ok = logger:remove_handler(?MODULE), + {error,{not_found,?MODULE}} = logger:remove_handler(?MODULE), + + {error, + {handler_not_added, + {invalid_config,logger_std_h,{type,faulty_type}}}} = + logger:add_handler(?MODULE,logger_std_h, + #{config => #{type => faulty_type}}), + + case os:type() of + {win32,_} -> + %% No use in testing file access on windows + ok; + _ -> + NoDir = lists:concat(["/",?MODULE,"_dir"]), + {error, + {handler_not_added,{{open_failed,NoDir,eacces},_}}} = + logger:add_handler(myh2,logger_std_h, + #{config=>#{type=>{file,NoDir}}}) + end, + + {error, + {handler_not_added,{{open_failed,Log,_},_}}} = + logger:add_handler(myh3,logger_std_h, + #{config=>#{type=>{file,Log,[bad_file_opt]}}}), + + ok = logger:notice(?msg). + +errors(cleanup,_Config) -> + logger:remove_handler(?MODULE). + +formatter_fail(Config) -> + Dir = ?config(priv_dir,Config), + Log = filename:join(Dir,?FUNCTION_NAME), + + %% no formatter + ok = logger:add_handler(?MODULE, + logger_std_h, + #{config => #{type => {file,Log}}, + filter_default=>stop, + filters=>?DEFAULT_HANDLER_FILTERS([?MODULE])}), + Pid = whereis(h_proc_name()), + true = is_pid(Pid), + H = logger:get_handler_ids(), + true = lists:member(?MODULE,H), + + %% Formatter is added automatically + {ok,#{formatter:={logger_formatter,_}}} = logger:get_handler_config(?MODULE), + logger:notice(M1=?msg,?domain), + Got1 = try_match_file(Log,"[0-9\\+\\-T:\\.]* notice: "++M1,5000), + + ok = logger:set_handler_config(?MODULE,formatter,{nonexistingmodule,#{}}), + logger:notice(M2=?msg,?domain), + Got2 = try_match_file(Log, + escape(Got1)++"[0-9\\+\\-T:\\.]* notice: FORMATTER CRASH: .*"++M2, + 5000), + + ok = logger:set_handler_config(?MODULE,formatter,{?MODULE,crash}), + logger:notice(M3=?msg,?domain), + Got3 = try_match_file(Log, + escape(Got2)++"[0-9\\+\\-T:\\.]* notice: FORMATTER CRASH: .*"++M3, + 5000), + + ok = logger:set_handler_config(?MODULE,formatter,{?MODULE,bad_return}), + logger:notice(?msg,?domain), + try_match_file(Log, + escape(Got3)++"FORMATTER ERROR: bad return value", + 5000), + + %% Check that handler is still alive and was never dead + Pid = whereis(h_proc_name()), + H = logger:get_handler_ids(), + + ok. + +formatter_fail(cleanup,_Config) -> + logger:remove_handler(?MODULE). + +config_fail(_Config) -> + {error,{handler_not_added,{invalid_config,logger_std_h,{bad,bad}}}} = + logger:add_handler(?MODULE,logger_std_h, + #{config => #{bad => bad}, + filter_default=>log, + formatter=>{?MODULE,self()}}), + {error,{handler_not_added,{invalid_config,logger_std_h, + {restart_type,bad}}}} = + logger:add_handler(?MODULE,logger_std_h, + #{config => #{restart_type => bad}, + filter_default=>log, + formatter=>{?MODULE,self()}}), + {error,{handler_not_added,{invalid_levels,{_,1,_}}}} = + logger:add_handler(?MODULE,logger_std_h, + #{config => #{drop_mode_qlen=>1}}), + {error,{handler_not_added,{invalid_levels,{43,42,_}}}} = + logger:add_handler(?MODULE,logger_std_h, + #{config => #{sync_mode_qlen=>43, + drop_mode_qlen=>42}}), + {error,{handler_not_added,{invalid_levels,{_,43,42}}}} = + logger:add_handler(?MODULE,logger_std_h, + #{config => #{drop_mode_qlen=>43, + flush_qlen=>42}}), + + ok = logger:add_handler(?MODULE,logger_std_h, + #{filter_default=>log, + formatter=>{?MODULE,self()}}), + {error,{illegal_config_change,_,_}} = + logger:set_handler_config(?MODULE,config, + #{type=>{file,"file"}}), + {error,{illegal_config_change,_,_}} = + logger:set_handler_config(?MODULE,id,bad), + {error,{invalid_levels,_}} = + logger:set_handler_config(?MODULE,config, + #{sync_mode_qlen=>100, + flush_qlen=>99}), + {error,{invalid_config,logger_std_h,{filesync_rep_int,2000}}} = + logger:set_handler_config(?MODULE, config, + #{filesync_rep_int => 2000}), + ok. + +config_fail(cleanup,_Config) -> + logger:remove_handler(?MODULE). + +crash_std_h_to_file(Config) -> + Dir = ?config(priv_dir,Config), + Log = filename:join(Dir,lists:concat([?MODULE,"_",?FUNCTION_NAME,".log"])), + crash_std_h(Config,?FUNCTION_NAME, + [{handler,default,logger_std_h, + #{ config => #{ type => {file, Log} }}}], + file, Log). +crash_std_h_to_file(cleanup,_Config) -> + crash_std_h(cleanup). + +crash_std_h_to_disk_log(Config) -> + Dir = ?config(priv_dir,Config), + Log = filename:join(Dir,lists:concat([?MODULE,"_",?FUNCTION_NAME,".log"])), + crash_std_h(Config,?FUNCTION_NAME, + [{handler,default,logger_disk_log_h, + #{ config => #{ file => Log }}}], + disk_log,Log). +crash_std_h_to_disk_log(cleanup,_Config) -> + crash_std_h(cleanup). + +crash_std_h(Config,Func,Var,Type,Log) -> + Dir = ?config(priv_dir,Config), + SysConfig = filename:join(Dir,lists:concat([?MODULE,"_",Func,".config"])), + ok = file:write_file(SysConfig, io_lib:format("[{kernel,[{logger,~p}]}].",[Var])), + Pa = filename:dirname(code:which(?MODULE)), + Name = lists:concat([?MODULE,"_",Func]), + Args = lists:concat([" -config ",filename:rootname(SysConfig)," -pa ",Pa]), + ct:pal("Starting ~p with ~tp", [Name,Args]), + %% Start a node which prints kernel logs to the destination specified by Type + {ok,Node} = test_server:start_node(Name, peer, [{args, Args}]), + HProcName = + case Type of + file -> ?name_to_reg_name(logger_std_h,?STANDARD_HANDLER); + disk_log -> ?name_to_reg_name(logger_disk_log_h,?STANDARD_HANDLER) + end, + Pid = rpc:call(Node,erlang,whereis,[HProcName]), + ok = rpc:call(Node,logger,set_handler_config,[?STANDARD_HANDLER,formatter, + {?MODULE,self()}]), + ok = log_on_remote_node(Node,"dummy1"), + ?check("dummy1"), + {ok,Bin1} = sync_and_read(Node,Type,Log), + <<"dummy1\n">> = binary:part(Bin1,{byte_size(Bin1),-7}), + + %% Kill the logger_std_h process + exit(Pid, kill), + + %% Wait a bit, then check that it is gone + timer:sleep(2000), + undefined = rpc:call(Node,erlang,whereis,[HProcName]), + + %% Check that file is not empty + {ok,Bin2} = sync_and_read(Node,Type,Log), + <<"dummy1\n">> = binary:part(Bin2,{byte_size(Bin2),-7}), + ok. + +%% Can not use rpc:call here, since the code would execute on a +%% process with group_leader on this (the calling) node, and thus +%% logger would send the log event to the logger process here instead +%% of logging it itself. +log_on_remote_node(Node,Msg) -> + _ = spawn_link(Node, + fun() -> erlang:group_leader(whereis(user),self()), + logger:notice(Msg) + end), + ok. + + +crash_std_h(cleanup) -> + Nodes = nodes(), + [test_server:stop_node(Node) || Node <- Nodes]. + +sync_and_read(Node,disk_log,Log) -> + rpc:call(Node,logger_disk_log_h,filesync,[?STANDARD_HANDLER]), + case file:read_file(Log ++ ".1") of + {ok,<<>>} -> + timer:sleep(5000), + file:read_file(Log ++ ".1"); + Ok -> + Ok + end; +sync_and_read(Node,file,Log) -> + rpc:call(Node,logger_std_h,filesync,[?STANDARD_HANDLER]), + case file:read_file(Log) of + {ok,<<>>} -> + timer:sleep(5000), + file:read_file(Log); + Ok -> + Ok + end. + +bad_input(_Config) -> + {error,{badarg,{filesync,["BadType"]}}} = logger_std_h:filesync("BadType"), + {error,{badarg,{info,["BadType"]}}} = logger_std_h:info("BadType"), + {error,{badarg,{reset,["BadType"]}}} = logger_std_h:reset("BadType"). + + +info_and_reset(_Config) -> + #{id := ?STANDARD_HANDLER} = logger_std_h:info(?STANDARD_HANDLER), + ok = logger_std_h:reset(?STANDARD_HANDLER). + +reconfig(Config) -> + Dir = ?config(priv_dir,Config), + ok = logger:add_handler(?MODULE, + logger_std_h, + #{config => #{type => standard_io}, + filter_default=>log, + filters=>?DEFAULT_HANDLER_FILTERS([?MODULE]), + formatter=>{?MODULE,self()}}), + #{id := ?MODULE, + type := standard_io, + file_ctrl_pid := FileCtrlPid, + sync_mode_qlen := ?SYNC_MODE_QLEN, + drop_mode_qlen := ?DROP_MODE_QLEN, + flush_qlen := ?FLUSH_QLEN, + burst_limit_enable := ?BURST_LIMIT_ENABLE, + burst_limit_max_count := ?BURST_LIMIT_MAX_COUNT, + burst_limit_window_time := ?BURST_LIMIT_WINDOW_TIME, + overload_kill_enable := ?OVERLOAD_KILL_ENABLE, + overload_kill_qlen := ?OVERLOAD_KILL_QLEN, + overload_kill_mem_size := ?OVERLOAD_KILL_MEM_SIZE, + overload_kill_restart_after := ?OVERLOAD_KILL_RESTART_AFTER, + filesync_repeat_interval := ?FILESYNC_REPEAT_INTERVAL} = + logger_std_h:info(?MODULE), + + ok = logger:set_handler_config(?MODULE, config, + #{sync_mode_qlen => 1, + drop_mode_qlen => 2, + flush_qlen => 3, + burst_limit_enable => false, + burst_limit_max_count => 10, + burst_limit_window_time => 10, + overload_kill_enable => true, + overload_kill_qlen => 100000, + overload_kill_mem_size => 10000000, + overload_kill_restart_after => infinity, + filesync_repeat_interval => no_repeat}), + #{id := ?MODULE, + type := standard_io, + file_ctrl_pid := FileCtrlPid, + sync_mode_qlen := 1, + drop_mode_qlen := 2, + flush_qlen := 3, + burst_limit_enable := false, + burst_limit_max_count := 10, + burst_limit_window_time := 10, + overload_kill_enable := true, + overload_kill_qlen := 100000, + overload_kill_mem_size := 10000000, + overload_kill_restart_after := infinity, + filesync_repeat_interval := no_repeat} = logger_std_h:info(?MODULE), + ok. + +reconfig(cleanup, _Config) -> + logger:remove_handler(?MODULE). + + +file_opts(Config) -> + Dir = ?config(priv_dir,Config), + Log = filename:join(Dir, lists:concat([?FUNCTION_NAME,".log"])), + BadFileOpts = [raw], + BadType = {file,Log,BadFileOpts}, + {error,{handler_not_added,{{open_failed,Log,enoent},_}}} = + logger:add_handler(?MODULE, logger_std_h, + #{config => #{type => BadType}}), + + OkFileOpts = [raw,append], + OkType = {file,Log,OkFileOpts}, + ok = logger:add_handler(?MODULE, + logger_std_h, + #{config => #{type => OkType}, + filter_default=>log, + filters=>?DEFAULT_HANDLER_FILTERS([?MODULE]), + formatter=>{?MODULE,self()}}), + + #{type := OkType} = logger_std_h:info(?MODULE), + logger:notice(M1=?msg,?domain), + ?check(M1), + B1 = ?bin(M1), + try_read_file(Log, {ok,B1}, filesync_rep_int()), + ok. +file_opts(cleanup, _Config) -> + logger:remove_handler(?MODULE). + + +sync(Config) -> + Dir = ?config(priv_dir,Config), + Log = filename:join(Dir, lists:concat([?FUNCTION_NAME,".log"])), + Type = {file,Log}, + ok = logger:add_handler(?MODULE, + logger_std_h, + #{config => #{type => Type}, + filter_default=>log, + filters=>?DEFAULT_HANDLER_FILTERS([?MODULE]), + formatter=>{?MODULE,nl}}), + + %% check repeated filesync happens + start_tracer([{logger_std_h, write_to_dev, 5}, + {logger_std_h, sync_dev, 4}, + {file, datasync, 1}], + [{logger_std_h, write_to_dev, <<"first\n">>}, + {logger_std_h, sync_dev}, + {file,datasync}]), + + logger:notice("first", ?domain), + %% wait for automatic filesync + check_tracer(filesync_rep_int()*2), + + %% check that explicit filesync is only done once + start_tracer([{logger_std_h, write_to_dev, 5}, + {logger_std_h, sync_dev, 4}, + {file, datasync, 1}], + [{logger_std_h, write_to_dev, <<"second\n">>}, + {logger_std_h, sync_dev}, + {file,datasync}, + {no_more,500} + ]), + logger:notice("second", ?domain), + %% do explicit sync + logger_std_h:filesync(?MODULE), + %% a second sync should be ignored + logger_std_h:filesync(?MODULE), + check_tracer(100), + + %% check that if there's no repeated filesync active, + %% a filesync is still performed when handler goes idle + logger:set_handler_config(?MODULE, config, + #{filesync_repeat_interval => no_repeat}), + no_repeat = maps:get(filesync_repeat_interval, logger_std_h:info(?MODULE)), + %% The following timer is to make sure the time from last log + %% ("second") to next ("third") is long enough, so the a flush is + %% triggered by the idle timeout between "thrid" and "fourth". + timer:sleep(?IDLE_DETECT_TIME_MSEC*2), + start_tracer([{logger_std_h, write_to_dev, 5}, + {logger_std_h, sync_dev, 4}, + {file, datasync, 1}], + [{logger_std_h, write_to_dev, <<"third\n">>}, + {logger_std_h, sync_dev}, + {file,datasync}, + {logger_std_h, write_to_dev, <<"fourth\n">>}, + {logger_std_h, sync_dev}, + {file,datasync}]), + logger:notice("third", ?domain), + %% wait for automatic filesync + timer:sleep(?IDLE_DETECT_TIME_MSEC*2), + logger:notice("fourth", ?domain), + %% wait for automatic filesync + check_tracer(?IDLE_DETECT_TIME_MSEC*2), + + %% switch repeated filesync on and verify that the looping works + SyncInt = 1000, + WaitT = 4500, + OneSync = {logger_std_h,handle_cast,repeated_filesync}, + %% receive 1 initial repeated_filesync, then 1 per sec + start_tracer([{logger_std_h,handle_cast,2}], + [OneSync || _ <- lists:seq(1, 1 + trunc(WaitT/SyncInt))]), + + logger:set_handler_config(?MODULE, config, + #{filesync_repeat_interval => SyncInt}), + SyncInt = maps:get(filesync_repeat_interval, logger_std_h:info(?MODULE)), + timer:sleep(WaitT), + logger:set_handler_config(?MODULE, config, + #{filesync_repeat_interval => no_repeat}), + check_tracer(100), + ok. +sync(cleanup, _Config) -> + dbg:stop_clear(), + logger:remove_handler(?MODULE). + +write_failure(Config) -> + Dir = ?config(priv_dir, Config), + File = lists:concat([?MODULE,"_",?FUNCTION_NAME,".log"]), + Log = filename:join(Dir, File), + Node = start_std_h_on_new_node(Config, Log), + false = (undefined == rpc:call(Node, ets, whereis, [?TEST_HOOKS_TAB])), + rpc:call(Node, ets, insert, [?TEST_HOOKS_TAB,{tester,self()}]), + rpc:call(Node, ?MODULE, set_internal_log, [?MODULE,internal_log]), + rpc:call(Node, ?MODULE, set_result, [file_write,ok]), + + ok = log_on_remote_node(Node, "Logged1"), + rpc:call(Node, logger_std_h, filesync, [?STANDARD_HANDLER]), + ?check_no_log, + try_read_file(Log, {ok,<<"Logged1\n">>}, filesync_rep_int()), + + rpc:call(Node, ?MODULE, set_result, [file_write,{error,terminated}]), + ok = log_on_remote_node(Node, "Cause simple error printout"), + + ?check({error,{?STANDARD_HANDLER,write,Log,{error,terminated}}}), + + ok = log_on_remote_node(Node, "No second error printout"), + ?check_no_log, + + rpc:call(Node, ?MODULE, set_result, [file_write,{error,eacces}]), + ok = log_on_remote_node(Node, "Cause simple error printout"), + ?check({error,{?STANDARD_HANDLER,write,Log,{error,eacces}}}), + + rpc:call(Node, ?MODULE, set_result, [file_write,ok]), + ok = log_on_remote_node(Node, "Logged2"), + rpc:call(Node, logger_std_h, filesync, [?STANDARD_HANDLER]), + ?check_no_log, + try_read_file(Log, {ok,<<"Logged1\nLogged2\n">>}, filesync_rep_int()), + ok. +write_failure(cleanup, _Config) -> + Nodes = nodes(), + [test_server:stop_node(Node) || Node <- Nodes]. + +sync_failure(Config) -> + Dir = ?config(priv_dir, Config), + File = lists:concat([?MODULE,"_",?FUNCTION_NAME,".log"]), + Log = filename:join(Dir, File), + Node = start_std_h_on_new_node(Config, Log), + false = (undefined == rpc:call(Node, ets, whereis, [?TEST_HOOKS_TAB])), + rpc:call(Node, ets, insert, [?TEST_HOOKS_TAB,{tester,self()}]), + rpc:call(Node, ?MODULE, set_internal_log, [?MODULE,internal_log]), + rpc:call(Node, ?MODULE, set_result, [file_datasync,ok]), + + SyncInt = 500, + ok = rpc:call(Node, logger, set_handler_config, + [?STANDARD_HANDLER, config, + #{filesync_repeat_interval => SyncInt}]), + Info = rpc:call(Node, logger_std_h, info, [?STANDARD_HANDLER]), + SyncInt = maps:get(filesync_repeat_interval, Info), + + ok = log_on_remote_node(Node, "Logged1"), + ?check_no_log, + + rpc:call(Node, ?MODULE, set_result, [file_datasync,{error,terminated}]), + ok = log_on_remote_node(Node, "Cause simple error printout"), + + ?check({error,{?STANDARD_HANDLER,filesync,Log,{error,terminated}}}), + + ok = log_on_remote_node(Node, "No second error printout"), + ?check_no_log, + + rpc:call(Node, ?MODULE, set_result, [file_datasync,{error,eacces}]), + ok = log_on_remote_node(Node, "Cause simple error printout"), + ?check({error,{?STANDARD_HANDLER,filesync,Log,{error,eacces}}}), + + rpc:call(Node, ?MODULE, set_result, [file_datasync,ok]), + ok = log_on_remote_node(Node, "Logged2"), + ?check_no_log, + ok. +sync_failure(cleanup, _Config) -> + Nodes = nodes(), + [test_server:stop_node(Node) || Node <- Nodes]. + +start_std_h_on_new_node(Config, Log) -> + {ok,_,Node} = + logger_test_lib:setup( + Config, + [{logger,[{handler,default,logger_std_h, + #{ config => #{ type => {file,Log}}}}]}]), + ok = rpc:call(Node,logger,set_handler_config,[?STANDARD_HANDLER,formatter, + {?MODULE,nl}]), + Node. + +%% functions for test hook macros to be called by rpc +set_internal_log(_Mod, _Func) -> + ?set_internal_log({_Mod,_Func}). +set_result(_Op, _Result) -> + ?set_result(_Op, _Result). +set_defaults() -> + ?set_defaults(). + +%% internal log function that sends the term to the test case process +internal_log(Type, Term) -> + [{tester,Tester}] = ets:lookup(?TEST_HOOKS_TAB, tester), + Tester ! {log,{Type,Term}}, + logger:internal_log(Type, Term), + ok. + + +%%%----------------------------------------------------------------- +%%% Overload protection tests + +op_switch_to_sync_file(Config) -> + {Log,HConfig,StdHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), + NumOfReqs = 500, + NewHConfig = + HConfig#{config => StdHConfig#{sync_mode_qlen => 2, + drop_mode_qlen => NumOfReqs+1, + flush_qlen => 2*NumOfReqs, + burst_limit_enable => false}}, + ok = logger:set_handler_config(?MODULE, NewHConfig), + %% TRecvPid = start_op_trace(), + send_burst({n,NumOfReqs}, seq, {chars,79}, notice), + Lines = count_lines(Log), + %% true = analyse_trace(TRecvPid, + %% fun(Events) -> find_mode(async,Events) end), + %% true = analyse_trace(TRecvPid, + %% fun(Events) -> find_mode(sync,Events) end), + %% true = analyse_trace(TRecvPid, + %% fun(Events) -> find_switch(async,sync,Events) end), + %% false = analyse_trace(TRecvPid, + %% fun(Events) -> find_mode(drop,Events) end), + %% false = analyse_trace(TRecvPid, + %% fun(Events) -> find_mode(flush,Events) end), + %% stop_op_trace(TRecvPid), + NumOfReqs = Lines, + ok = file_delete(Log), + ok. +op_switch_to_sync_file(cleanup, _Config) -> + ok = stop_handler(?MODULE). + +op_switch_to_sync_tty(Config) -> + {HConfig,StdHConfig} = start_handler(?MODULE, standard_io, Config), + NumOfReqs = 500, + NewHConfig = + HConfig#{config => StdHConfig#{sync_mode_qlen => 3, + drop_mode_qlen => NumOfReqs+1, + flush_qlen => 2*NumOfReqs, + burst_limit_enable => false}}, + ok = logger:set_handler_config(?MODULE, NewHConfig), + send_burst({n,NumOfReqs}, seq, {chars,79}, notice), + ok. +op_switch_to_sync_tty(cleanup, _Config) -> + ok = stop_handler(?MODULE). + +op_switch_to_drop_file() -> + [{timetrap,{seconds,180}}]. +op_switch_to_drop_file(Config) -> + Test = + fun() -> + {Log,HConfig,StdHConfig} = + start_handler(?MODULE, ?FUNCTION_NAME, Config), + NumOfReqs = 300, + Procs = 2, + Bursts = 10, + NewHConfig = + HConfig#{config => + StdHConfig#{sync_mode_qlen => 1, + drop_mode_qlen => 2, + flush_qlen => + Procs*NumOfReqs*Bursts, + burst_limit_enable => false}}, + ok = logger:set_handler_config(?MODULE, NewHConfig), + %% It sometimes happens that the handler gets the + %% requests in a slow enough pace so that dropping + %% never occurs. Therefore, lets generate a number of + %% bursts to increase the chance of message buildup. + [send_burst({n,NumOfReqs}, {spawn,Procs,0}, {chars,79}, notice) || + _ <- lists:seq(1, Bursts)], + Logged = count_lines(Log), + ok = stop_handler(?MODULE), + ct:pal("Number of messages dropped = ~w (~w)", + [Procs*NumOfReqs*Bursts-Logged,Procs*NumOfReqs*Bursts]), + true = (Logged < (Procs*NumOfReqs*Bursts)), + true = (Logged > 0), + _ = file_delete(Log), + ok + end, + %% As it's tricky to get the timing right in only one go, we perform the + %% test repeatedly, hoping that will generate a successful result. + case repeat_until_ok(Test, 10) of + {ok,{Failures,_Result}} -> + ct:log("Failed ~w times before success!", [Failures]); + {fails,Reason} -> + ct:fail(Reason) + end. +op_switch_to_drop_file(cleanup, _Config) -> + _ = stop_handler(?MODULE). + +op_switch_to_drop_tty(Config) -> + {HConfig,StdHConfig} = start_handler(?MODULE, standard_io, Config), + NumOfReqs = 300, + Procs = 2, + NewHConfig = + HConfig#{config => StdHConfig#{sync_mode_qlen => 1, + drop_mode_qlen => 2, + flush_qlen => + Procs*NumOfReqs+1, + burst_limit_enable => false}}, + ok = logger:set_handler_config(?MODULE, NewHConfig), + send_burst({n,NumOfReqs}, {spawn,Procs,0}, {chars,79}, notice), + ok. +op_switch_to_drop_tty(cleanup, _Config) -> + ok = stop_handler(?MODULE). + +op_switch_to_flush_file() -> + [{timetrap,{minutes,5}}]. +op_switch_to_flush_file(Config) -> + Test = + fun() -> + {Log,HConfig,StdHConfig} = + start_handler(?MODULE, ?FUNCTION_NAME, Config), + + %% NOTE: it's important that both async and sync + %% requests have been queued when the flush happens + %% (verify with coverage of flush_log_requests/2) + + NewHConfig = + HConfig#{config => + StdHConfig#{sync_mode_qlen => 2, + %% disable drop mode + drop_mode_qlen => 300, + flush_qlen => 300, + burst_limit_enable => false}}, + ok = logger:set_handler_config(?MODULE, NewHConfig), + NumOfReqs = 1500, + Procs = 10, + Bursts = 10, + %% It sometimes happens that the handler either gets + %% the requests in a slow enough pace so that flushing + %% never occurs, or it gets all messages at once, + %% causing all messages to get flushed (no dropping of + %% sync messages gets tested). Therefore, lets + %% generate a number of bursts to increase the chance + %% of message buildup in some random fashion. + [send_burst({n,NumOfReqs}, {spawn,Procs,0}, {chars,79}, notice) || + _ <- lists:seq(1,Bursts)], + Logged = count_lines(Log), + ok = stop_handler(?MODULE), + ct:pal("Number of messages flushed/dropped = ~w (~w)", + [NumOfReqs*Procs*Bursts-Logged,NumOfReqs*Procs*Bursts]), + true = (Logged < (NumOfReqs*Procs*Bursts)), + true = (Logged > 0), + _ = file_delete(Log), + ok + end, + %% As it's tricky to get the timing right in only one go, we perform the + %% test repeatedly, hoping that will generate a successful result. + case repeat_until_ok(Test, 10) of + {ok,{Failures,_Result}} -> + ct:log("Failed ~w times before success!", [Failures]); + {fails,Reason} -> + ct:fail(Reason) + end. +op_switch_to_flush_file(cleanup, _Config) -> + _ = stop_handler(?MODULE). + +op_switch_to_flush_tty() -> + [{timetrap,{minutes,5}}]. +op_switch_to_flush_tty(Config) -> + {HConfig,StdHConfig} = start_handler(?MODULE, standard_io, Config), + + %% it's important that both async and sync requests have been queued + %% when the flush happens (verify with coverage of flush_log_requests/2) + + NewHConfig = + HConfig#{config => StdHConfig#{sync_mode_qlen => 2, + %% disable drop mode + drop_mode_qlen => 100, + flush_qlen => 100, + burst_limit_enable => false}}, + ok = logger:set_handler_config(?MODULE, NewHConfig), + NumOfReqs = 1000, + Procs = 100, + send_burst({n,NumOfReqs}, {spawn,Procs,0}, {chars,79}, notice), + ok. +op_switch_to_flush_tty(cleanup, _Config) -> + ok = stop_handler(?MODULE). + +limit_burst_disabled(Config) -> + {Log,HConfig,StdHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), + NewHConfig = + HConfig#{config => StdHConfig#{burst_limit_enable => false, + burst_limit_max_count => 10, + burst_limit_window_time => 2000, + drop_mode_qlen => 200, + flush_qlen => 300}}, + ok = logger:set_handler_config(?MODULE, NewHConfig), + NumOfReqs = 100, + send_burst({n,NumOfReqs}, seq, {chars,79}, notice), + Logged = count_lines(Log), + ct:pal("Number of messages logged = ~w", [Logged]), + NumOfReqs = Logged, + ok = file_delete(Log), + ok. +limit_burst_disabled(cleanup, _Config) -> + ok = stop_handler(?MODULE). + +limit_burst_enabled_one(Config) -> + {Log,HConfig,StdHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), + ReqLimit = 10, + NewHConfig = + HConfig#{config => StdHConfig#{burst_limit_enable => true, + burst_limit_max_count => ReqLimit, + burst_limit_window_time => 2000, + drop_mode_qlen => 200, + flush_qlen => 300}}, + ok = logger:set_handler_config(?MODULE, NewHConfig), + NumOfReqs = 100, + send_burst({n,NumOfReqs}, seq, {chars,79}, notice), + Logged = count_lines(Log), + ct:pal("Number of messages logged = ~w", [Logged]), + ReqLimit = Logged, + ok = file_delete(Log), + ok. +limit_burst_enabled_one(cleanup, _Config) -> + ok = stop_handler(?MODULE). + +limit_burst_enabled_period(Config) -> + {Log,HConfig,StdHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), + ReqLimit = 10, + BurstTWin = 1000, + NewHConfig = + HConfig#{config => StdHConfig#{burst_limit_enable => true, + burst_limit_max_count => ReqLimit, + burst_limit_window_time => BurstTWin, + drop_mode_qlen => 20000, + flush_qlen => 20001}}, + ok = logger:set_handler_config(?MODULE, NewHConfig), + + Windows = 3, + Sent = send_burst({t,BurstTWin*Windows}, seq, {chars,79}, notice), + Logged = count_lines(Log), + ct:pal("Number of messages sent = ~w~nNumber of messages logged = ~w", + [Sent,Logged]), + true = (Logged > (ReqLimit*Windows)) andalso + (Logged < (ReqLimit*(Windows+2))), + ok = file_delete(Log), + ok. +limit_burst_enabled_period(cleanup, _Config) -> + ok = stop_handler(?MODULE). + +kill_disabled(Config) -> + {Log,HConfig,StdHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), + NewHConfig = + HConfig#{config=>StdHConfig#{overload_kill_enable=>false, + overload_kill_qlen=>10, + overload_kill_mem_size=>100}}, + ok = logger:set_handler_config(?MODULE, NewHConfig), + NumOfReqs = 100, + send_burst({n,NumOfReqs}, seq, {chars,79}, notice), + Logged = count_lines(Log), + ct:pal("Number of messages logged = ~w", [Logged]), + ok = file_delete(Log), + true = is_pid(whereis(h_proc_name())), + ok. +kill_disabled(cleanup, _Config) -> + ok = stop_handler(?MODULE). + +qlen_kill_new(Config) -> + {Log,HConfig,StdHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), + Pid0 = whereis(h_proc_name()), + {_,Mem0} = process_info(Pid0, memory), + RestartAfter = ?OVERLOAD_KILL_RESTART_AFTER, + NewHConfig = + HConfig#{config=>StdHConfig#{overload_kill_enable=>true, + overload_kill_qlen=>10, + overload_kill_mem_size=>Mem0+50000, + overload_kill_restart_after=>RestartAfter}}, + ok = logger:set_handler_config(?MODULE, NewHConfig), + MRef = erlang:monitor(process, Pid0), + NumOfReqs = 100, + Procs = 4, + send_burst({n,NumOfReqs}, {spawn,Procs,0}, {chars,79}, notice), + %% send_burst({n,NumOfReqs}, seq, {chars,79}, notice), + receive + {'DOWN', MRef, _, _, Info} -> + case Info of + {shutdown,{overloaded,?MODULE,QLen,Mem}} -> + ct:pal("Terminated with qlen = ~w, mem = ~w", [QLen,Mem]); + killed -> + ct:pal("Slow shutdown, handler process was killed!", []) + end, + file_delete(Log), + {ok,_} = wait_for_process_up(RestartAfter * 3), + ok + after + 5000 -> + Info = logger_std_h:info(?MODULE), + ct:pal("Handler state = ~p", [Info]), + ct:fail("Handler not dead! It should not have survived this!") + end. +qlen_kill_new(cleanup, _Config) -> + ok = stop_handler(?MODULE). + +%% choke the standard handler on remote node to verify the termination +%% works as expected +qlen_kill_std(_Config) -> + %%! HERE + %% Dir = ?config(priv_dir, Config), + %% File = lists:concat([?MODULE,"_",?FUNCTION_NAME,".log"]), + %% Log = filename:join(Dir, File), + %% Node = start_std_h_on_new_node(Config, ?FUNCTION_NAME, Log), + %% ok = rpc:call(Node, logger, set_handler_config, + %% [?STANDARD_HANDLER, config, + %% #{overload_kill_enable=>true, + %% overload_kill_qlen=>10, + %% overload_kill_mem_size=>100000}]), + {skip,"Not done yet"}. + +mem_kill_new(Config) -> + {Log,HConfig,StdHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), + Pid0 = whereis(h_proc_name()), + {_,Mem0} = process_info(Pid0, memory), + RestartAfter = ?OVERLOAD_KILL_RESTART_AFTER, + NewHConfig = + HConfig#{config=>StdHConfig#{overload_kill_enable=>true, + overload_kill_qlen=>50000, + overload_kill_mem_size=>Mem0+500, + overload_kill_restart_after=>RestartAfter}}, + ok = logger:set_handler_config(?MODULE, NewHConfig), + MRef = erlang:monitor(process, Pid0), + NumOfReqs = 100, + Procs = 4, + send_burst({n,NumOfReqs}, {spawn,Procs,0}, {chars,79}, notice), + %% send_burst({n,NumOfReqs}, seq, {chars,79}, notice), + receive + {'DOWN', MRef, _, _, Info} -> + case Info of + {shutdown,{overloaded,?MODULE,QLen,Mem}} -> + ct:pal("Terminated with qlen = ~w, mem = ~w", [QLen,Mem]); + killed -> + ct:pal("Slow shutdown, handler process was killed!", []) + end, + file_delete(Log), + {ok,_} = wait_for_process_up(RestartAfter * 3), + ok + after + 5000 -> + Info = logger_std_h:info(?MODULE), + ct:pal("Handler state = ~p", [Info]), + ct:fail("Handler not dead! It should not have survived this!") + end. +mem_kill_new(cleanup, _Config) -> + ok = stop_handler(?MODULE). + +%% choke the standard handler on remote node to verify the termination +%% works as expected +mem_kill_std(_Config) -> + {skip,"Not done yet"}. + +restart_after() -> + [{timetrap,{minutes,2}}]. +restart_after(Config) -> + {Log,HConfig,StdHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), + NewHConfig1 = + HConfig#{config=>StdHConfig#{overload_kill_enable=>true, + overload_kill_qlen=>10, + overload_kill_restart_after=>infinity}}, + ok = logger:set_handler_config(?MODULE, NewHConfig1), + MRef1 = erlang:monitor(process, whereis(h_proc_name())), + %% kill handler + send_burst({n,100}, {spawn,4,0}, {chars,79}, notice), + receive + {'DOWN', MRef1, _, _, _Reason1} -> + file_delete(Log), + error = wait_for_process_up(?OVERLOAD_KILL_RESTART_AFTER * 3), + ok + after + 5000 -> + Info1 = logger_std_h:info(?MODULE), + ct:pal("Handler state = ~p", [Info1]), + ct:fail("Handler not dead! It should not have survived this!") + end, + + {Log,_,_} = start_handler(?MODULE, ?FUNCTION_NAME, Config), + RestartAfter = ?OVERLOAD_KILL_RESTART_AFTER, + NewHConfig2 = + HConfig#{config=>StdHConfig#{overload_kill_enable=>true, + overload_kill_qlen=>10, + overload_kill_restart_after=>RestartAfter}}, + ok = logger:set_handler_config(?MODULE, NewHConfig2), + Pid0 = whereis(h_proc_name()), + MRef2 = erlang:monitor(process, Pid0), + %% kill handler + send_burst({n,100}, {spawn,4,0}, {chars,79}, notice), + receive + {'DOWN', MRef2, _, _, _Reason2} -> + file_delete(Log), + {ok,Pid1} = wait_for_process_up(RestartAfter * 3), + false = (Pid1 == Pid0), + ok + after + 5000 -> + Info2 = logger_std_h:info(?MODULE), + ct:pal("Handler state = ~p", [Info2]), + ct:fail("Handler not dead! It should not have survived this!") + end, + ok. +restart_after(cleanup, _Config) -> + ok = stop_handler(?MODULE). + +%% send handler requests (sync, info, reset, change_config) +%% during high load to verify that sync, dropping and flushing is +%% handled correctly. +handler_requests_under_load() -> + [{timetrap,{minutes,3}}]. +handler_requests_under_load(Config) -> + {Log,HConfig,StdHConfig} = + start_handler(?MODULE, ?FUNCTION_NAME, Config), + NewHConfig = + HConfig#{config => StdHConfig#{sync_mode_qlen => 2, + drop_mode_qlen => 1000, + flush_qlen => 2000, + burst_limit_enable => false}}, + ok = logger:set_handler_config(?MODULE, NewHConfig), + Pid = spawn_link(fun() -> send_requests(?MODULE, 1, [{filesync,[]}, + {info,[]}, + {reset,[]}, + {change_config,[]}]) + end), + Sent = send_burst({t,10000}, seq, {chars,79}, notice), + Pid ! {self(),finish}, + ReqResult = receive {Pid,Result} -> Result end, + Logged = count_lines(Log), + ct:pal("Number of messages sent = ~w~nNumber of messages logged = ~w", + [Sent,Logged]), + FindError = fun(Res) -> + [E || E <- Res, + is_tuple(E) andalso (element(1,E) == error)] + end, + Errors = [{Req,FindError(Res)} || {Req,Res} <- ReqResult], + NoOfReqs = lists:foldl(fun({_,Res}, N) -> N + length(Res) end, 0, ReqResult), + ct:pal("~w requests made. Errors: ~n~p", [NoOfReqs,Errors]), + ok = file_delete(Log). +handler_requests_under_load(cleanup, _Config) -> + ok = stop_handler(?MODULE). + +send_requests(HName, TO, Reqs = [{Req,Res}|Rs]) -> + receive + {From,finish} -> + From ! {self(),Reqs} + after + TO -> + Result = + case Req of + change_config -> + logger:set_handler_config(HName, config, + #{overload_kill_enable => + false}); + Func -> + logger_std_h:Func(HName) + end, + send_requests(HName, TO, Rs ++ [{Req,[Result|Res]}]) + end. + + +%%%----------------------------------------------------------------- +%%% +start_handler(Name, TTY, Config) when TTY == standard_io; + TTY == standard_error-> + ok = logger:add_handler(Name, + logger_std_h, + #{config => #{type => TTY}, + filter_default=>log, + filters=>?DEFAULT_HANDLER_FILTERS([Name]), + formatter=>{?MODULE,op}}), + {ok,HConfig = #{config := StdHConfig}} = logger:get_handler_config(Name), + {HConfig,StdHConfig}; + +start_handler(Name, FuncName, Config) -> + Dir = ?config(priv_dir,Config), + Log = filename:join(Dir, lists:concat([FuncName,".log"])), + ct:pal("Logging to ~tp", [Log]), + Type = {file,Log}, + _ = file_delete(Log), + ok = logger:add_handler(Name, + logger_std_h, + #{config => #{type => Type}, + filter_default=>log, + filters=>?DEFAULT_HANDLER_FILTERS([Name]), + formatter=>{?MODULE,op}}), + {ok,HConfig = #{config := StdHConfig}} = logger:get_handler_config(Name), + {Log,HConfig,StdHConfig}. + +stop_handler(Name) -> + R = logger:remove_handler(Name), + ct:pal("Handler ~p stopped! Result: ~p", [Name,R]), + R. + +count_lines(File) -> + wait_until_written(File, -1), + count_lines1(File). + +wait_until_written(File, Sz) -> + timer:sleep(2000), + case file:read_file_info(File) of + {ok,#file_info{size = Sz}} -> + timer:sleep(1000), + case file:read_file_info(File) of + {ok,#file_info{size = Sz}} -> + ok; + {ok,#file_info{size = Sz1}} -> + wait_until_written(File, Sz1) + end; + {ok,#file_info{size = Sz1}} -> + wait_until_written(File, Sz1) + end. + +count_lines1(File) -> + {_,Dev} = file:open(File, [read]), + Lines = count_lines2(Dev, 0), + file:close(Dev), + Lines. + +count_lines2(Dev, LC) -> + case file:read_line(Dev) of + {ok,"Handler logger_std_h_SUITE " ++_} -> + %% Not counting handler info + count_lines2(Dev,LC); + {ok,_} -> + count_lines2(Dev,LC+1); + eof -> LC + end. + +send_burst(NorT, Type, {chars,Sz}, Class) -> + Text = [34 + rand:uniform(126-34) || _ <- lists:seq(1,Sz)], + case NorT of + {n,N} -> + %% process_flag(priority, high), + send_n_burst(N, Type, Text, Class), + %% process_flag(priority, normal), + N; + {t,T} -> + ct:pal("Sending messages sequentially for ~w ms", [T]), + T0 = erlang:monotonic_time(millisecond), + send_t_burst(T0, T, Text, Class, 0) + end. + +send_n_burst(0, _, _Text, _Class) -> + ok; +send_n_burst(N, seq, Text, Class) -> + ok = logger:Class(Text, ?domain), + send_n_burst(N-1, seq, Text, Class); +send_n_burst(N, {spawn,Ps,TO}, Text, Class) -> + ct:pal("~w processes each sending ~w messages", [Ps,N]), + MRefs = [begin if TO == 0 -> ok; true -> timer:sleep(TO) end, + monitor(process,spawn_link(per_proc_fun(N,Text,Class,X))) + end || X <- lists:seq(1,Ps)], + lists:foreach(fun(MRef) -> + receive + {'DOWN', MRef, _, _, _} -> + ok + end + end, MRefs), + ct:pal("Message burst sent", []), + ok. + +send_t_burst(T0, T, Text, Class, N) -> + T1 = erlang:monotonic_time(millisecond), + if (T1-T0) > T -> + N; + true -> + ok = logger:Class(Text, ?domain), + send_t_burst(T0, T, Text, Class, N+1) + end. + +per_proc_fun(N,Text,Class,X) when X rem 2 == 0 -> + fun() -> + process_flag(priority,high), + send_n_burst(N, seq, Text, Class) + end; +per_proc_fun(N,Text,Class,_) -> + fun() -> + send_n_burst(N, seq, Text, Class) + end. + +%%%----------------------------------------------------------------- +%%% Formatter callback +%%% Using this to send the formatted string back to the test case +%%% process - so it can check for logged events. +format(_,bad_return) -> + bad_return; +format(_,crash) -> + erlang:error(formatter_crashed); +format(#{msg:={string,String0}},no_nl) -> + String = unicode:characters_to_list(String0), + String; +format(#{msg:={string,String0}},nl) -> + String = unicode:characters_to_list(String0), + String++"\n"; +format(#{msg:={string,String0}},op) -> + String = unicode:characters_to_list(String0), + String++"\n"; +format(#{msg:={report,#{label:={supervisor,progress}}}},op) -> + ""; +format(#{msg:={report,#{label:={gen_server,terminate}}}},op) -> + ""; +format(#{msg:={report,#{label:={proc_lib,crash}}}},op) -> + ""; +format(#{msg:={F,A}},OpOrPid) when is_list(F), is_list(A) -> + String = lists:flatten(io_lib:format(F,A)), + if is_pid(OpOrPid) -> OpOrPid ! {log,String}; + true -> ok + end, + String++"\n"; +format(#{msg:={string,String0}},Pid) -> + String = unicode:characters_to_list(String0), + Pid ! {log,String}, + String++"\n". + +add_remove_instance_nofile(Type) -> + ok = logger:add_handler(?MODULE,logger_std_h, + #{config => #{type => Type}, + filter_default=>stop, + filters=>?DEFAULT_HANDLER_FILTERS([?MODULE]), + formatter=>{?MODULE,self()}}), + Pid = whereis(h_proc_name()), + true = is_pid(Pid), + group_leader(group_leader(),Pid), % to get printouts in test log + logger:notice(M1=?msg,?domain), + ?check(M1), + %% check that sync doesn't do damage even if not relevant + ok = logger_std_h:filesync(?MODULE), + ok = logger:remove_handler(?MODULE), + timer:sleep(500), + undefined = whereis(h_proc_name()), + logger:notice(?msg,?domain), + ?check_no_log, + ok. + +logger_std_h_remove() -> + logger:remove_handler(?MODULE). +logger_std_h_remove(Id) -> + logger:remove_handler(Id). + +try_read_file(FileName, Expected, Time) when Time > 0 -> + case file:read_file(FileName) of + Expected -> + ok; + Error = {error,_Reason} -> + ct:pal("Can't read ~tp: ~tp", [FileName,Error]), + erlang:error(Error); + Got -> + ct:pal("try_read_file got ~tp", [Got]), + timer:sleep(500), + try_read_file(FileName, Expected, Time-500) + end; +try_read_file(FileName, Expected, _) -> + ct:pal("Missing pattern ~tp in ~tp", [Expected,FileName]), + erlang:error({error,missing_expected_pattern}). + +try_match_file(FileName, Pattern, Time) -> + try_match_file(FileName, Pattern, Time, <<>>). + +try_match_file(FileName, Pattern, Time, _) when Time > 0 -> + case file:read_file(FileName) of + {ok, Bin} -> + case re:run(Bin,Pattern,[{capture,none}]) of + match -> + unicode:characters_to_list(Bin); + _ -> + timer:sleep(100), + try_match_file(FileName, Pattern, Time-100, Bin) + end; + Error -> + erlang:error(Error) + end; +try_match_file(_,Pattern,_,Incorrect) -> + ct:pal("try_match_file did not match pattern: ~p~nGot: ~p~n", + [Pattern,Incorrect]), + erlang:error({error,not_matching_pattern,Pattern,Incorrect}). + +repeat_until_ok(Fun, N) -> + repeat_until_ok(Fun, 0, N, undefined). + +repeat_until_ok(_Fun, Stop, Stop, Reason) -> + {fails,Reason}; + +repeat_until_ok(Fun, C, Stop, FirstReason) -> + if C > 0 -> timer:sleep(5000); + true -> ok + end, + try Fun() of + Result -> + {ok,{C,Result}} + catch + _:Reason:Stack -> + ct:pal("Test fails: ~p (~p)~n", [Reason,hd(Stack)]), + if FirstReason == undefined -> + repeat_until_ok(Fun, C+1, Stop, {Reason,Stack}); + true -> + repeat_until_ok(Fun, C+1, Stop, FirstReason) + end + end. + + +%%%----------------------------------------------------------------- +%%% +start_op_trace() -> + TraceFun = fun({trace,_,call,{_Mod,Func,Details}}, Pid) -> + Pid ! {trace_call,Func,Details}, + Pid; + ({trace,_,return_from,{_Mod,Func,_},RetVal}, Pid) -> + Pid ! {trace_return,Func,RetVal}, + Pid + end, + TRecvPid = spawn_link(fun() -> trace_receiver(5000) end), + {ok,_} = dbg:tracer(process, {TraceFun, TRecvPid}), + + {ok,_} = dbg:p(whereis(h_proc_name()), [c]), + {ok,_} = dbg:p(self(), [c]), + + MS1 = dbg:fun2ms(fun([_]) -> return_trace() end), + {ok,_} = dbg:tp(logger_h_common, check_load, 1, MS1), + + {ok,_} = dbg:tpl(logger_h_common, flush_log_requests, 2, []), + + MS2 = dbg:fun2ms(fun([_,mode]) -> return_trace() end), + {ok,_} = dbg:tpl(ets, lookup, 2, MS2), + + ct:pal("Tracing started!", []), + TRecvPid. + +stop_op_trace(TRecvPid) -> + dbg:stop_clear(), + unlink(TRecvPid), + exit(TRecvPid, kill), + ok. + +find_mode(flush, Events) -> + lists:any(fun({trace_call,flush_log_requests,[_,_]}) -> true; + (_) -> false + end, Events); +find_mode(Mode, Events) -> + lists:keymember([{mode,Mode}], 3, Events). + +%% find_switch(_From, To, Events) -> +%% try lists:foldl(fun({trace_return,check_load,{To,_,_,_}}, +%% {trace_call,check_load,[#{mode := From}]}) -> +%% throw(match); +%% (Event, _) -> +%% Event +%% end, undefined, Events) of +%% _ -> false +%% catch +%% throw:match -> true +%% end. + +analyse_trace(TRecvPid, TestFun) -> + TRecvPid ! {test,self(),TestFun}, + receive + {result,TRecvPid,Result} -> + Result + after + 60000 -> + fails + end. + +trace_receiver(IdleT) -> + Msgs = receive_until_idle(IdleT, 5, []), + ct:pal("~w trace events generated", [length(Msgs)]), + analyse(Msgs). + +receive_until_idle(IdleT, WaitN, Msgs) -> + receive + Msg = {trace_call,_,_} -> + receive_until_idle(IdleT, 5, [Msg | Msgs]); + Msg = {trace_return,_,_} -> + receive_until_idle(IdleT, 5, [Msg | Msgs]) + after + IdleT -> + if WaitN == 0 -> + Msgs; + true -> + receive_until_idle(IdleT, WaitN-1, Msgs) + end + end. + +analyse(Msgs) -> + receive + {test,From,TestFun} -> + From ! {result,self(),TestFun(Msgs)}, + analyse(Msgs) + end. + +start_tracer(Trace,Expected) -> + Pid = self(), + FileCtrlPid = maps:get(file_ctrl_pid, logger_std_h:info(?MODULE)), + dbg:tracer(process,{fun tracer/2,{Pid,Expected}}), + dbg:p(whereis(h_proc_name()),[c]), + dbg:p(FileCtrlPid,[c]), + tpl(Trace), + ok. + +tpl([{M,F,A}|Trace]) -> + {ok,Match} = dbg:tpl(M,F,A,[]), + case lists:keyfind(matched,1,Match) of + {_,_,1} -> + ok; + _ -> + dbg:stop_clear(), + throw({skip,"Can't trace "++atom_to_list(M)++":"++ + atom_to_list(F)++"/"++integer_to_list(A)}) + end, + tpl(Trace); +tpl([]) -> + ok. + +tracer({trace,_,call,{logger_std_h,handle_cast,[Op|_]}}, + {Pid,[{Mod,Func,Op}|Expected]}) -> + maybe_tracer_done(Pid,Expected,{Mod,Func,Op}); +tracer({trace,_,call,{Mod=logger_std_h,Func=write_to_dev,[_,Data,_,_,_]}}, + {Pid,[{Mod,Func,Data}|Expected]}) -> + maybe_tracer_done(Pid,Expected,{Mod,Func,Data}); +tracer({trace,_,call,{Mod,Func,_}}, {Pid,[{Mod,Func}|Expected]}) -> + maybe_tracer_done(Pid,Expected,{Mod,Func}); +tracer({trace,_,call,Call}, {Pid,Expected}) -> + ct:log("Tracer got unexpected: ~p~nExpected: ~p~n",[Call,Expected]), + Pid ! {tracer_got_unexpected,Call,Expected}, + {Pid,Expected}. + +maybe_tracer_done(Pid,[]=Expected,Got) -> + ct:log("Tracer got: ~p~n",[Got]), + Pid ! {tracer_done,0}, + {Pid,Expected}; +maybe_tracer_done(Pid,[{no_more,T}]=Expected,Got) -> + ct:log("Tracer got: ~p~n",[Got]), + Pid ! {tracer_done,T}, + {Pid,Expected}; +maybe_tracer_done(Pid,Expected,Got) -> + ct:log("Tracer got: ~p~n",[Got]), + {Pid,Expected}. + +check_tracer(T) -> + check_tracer(T,fun() -> ct:fail({timeout,tracer}) end). +check_tracer(T,TimeoutFun) -> + receive + {tracer_done,Delay} -> + %% Possibly wait Delay ms to check that no unexpected + %% traces are received + check_tracer(Delay,fun() -> ok end); + {tracer_got_unexpected,Got,Expected} -> + dbg:stop_clear(), + ct:fail({tracer_got_unexpected,Got,Expected}) + after T -> + dbg:stop_clear(), + TimeoutFun() + end. + +escape([$+|Rest]) -> + [$\\,$+|escape(Rest)]; +escape([H|T]) -> + [H|escape(T)]; +escape([]) -> + []. + +h_proc_name() -> + h_proc_name(?MODULE). +h_proc_name(Name) -> + ?name_to_reg_name(logger_std_h,Name). + +wait_for_process_up(T) -> + wait_for_process_up(?MODULE, h_proc_name(), T). + +wait_for_process_up(Name, RegName, T) -> + N = (T div 500) + 1, + wait_for_process_up1(Name, RegName, N). + +wait_for_process_up1(_Name, _RegName, 0) -> + error; +wait_for_process_up1(Name, RegName, N) -> + timer:sleep(500), + case whereis(RegName) of + Pid when is_pid(Pid) -> + case logger:get_handler_config(Name) of + {ok,_} -> + %% ct:pal("Process ~p up (~p tries left)",[Name,N]), + {ok,Pid}; + _ -> + wait_for_process_up1(Name, RegName, N-1) + end; + undefined -> + %% ct:pal("Waiting for process ~p (~p tries left)",[Name,N]), + wait_for_process_up1(Name, RegName, N-1) + end. + +filesync_rep_int() -> + case (fun() -> is_atom(?FILESYNC_REPEAT_INTERVAL) end)() of + true -> 5500; + false -> ?FILESYNC_REPEAT_INTERVAL + 500 + end. + + +file_delete(Log) -> + file:delete(Log). + diff --git a/lib/kernel/test/logger_test_lib.erl b/lib/kernel/test/logger_test_lib.erl new file mode 100644 index 0000000000..81eb9ce5eb --- /dev/null +++ b/lib/kernel/test/logger_test_lib.erl @@ -0,0 +1,82 @@ +% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2018. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% +-module(logger_test_lib). + +-include_lib("kernel/src/logger_internal.hrl"). + +-export([setup/2, log/3, sync_and_read/3]). + +-export([init/2, + pre_init_per_suite/3, pre_init_per_testcase/4, + post_end_per_testcase/5, post_end_per_suite/3]). + +setup(Config,Vars) -> + FuncStr = lists:concat([proplists:get_value(suite, Config), "_", + proplists:get_value(tc, Config)]), + ConfigFileName = filename:join(proplists:get_value(priv_dir, Config), FuncStr), + file:write_file(ConfigFileName ++ ".config", io_lib:format("[{kernel, ~p}].",[Vars])), + case test_server:start_node(proplists:get_value(tc, Config), slave, + [{args, ["-pa ",filename:dirname(code:which(?MODULE)), + " -boot start_sasl -kernel start_timer true " + "-config ",ConfigFileName]}]) of + {ok, Node} -> + L = rpc:call(Node, logger, get_config, []), + ct:log("~p",[L]), + {ok, L, Node}; + {error, Reason} -> + ct:log("Failed to start node: ~p",[Reason]), + error + end. + +log(Node, F, A) -> + log(Node, logger, F, A). +log(Node, M, F, A) -> + MD = #{ gl => rpc:call(Node, erlang, whereis, [logger]) }, + rpc:call(Node, M, F, A ++ [MD]). + +sync_and_read(Node,disk_log,Log) -> + rpc:call(Node,logger_disk_log_h,filesync,[?STANDARD_HANDLER]), + file:read_file(Log ++ ".1"); +sync_and_read(Node, file,Log) -> + ok = rpc:call(Node,logger_std_h,filesync,[?STANDARD_HANDLER]), + file:read_file(Log). + + +init(_, _) -> + {ok, []}. + +pre_init_per_suite(_Suite, Config, State) -> + {[{nodes, nodes()} | Config], State}. + +pre_init_per_testcase(Suite, TC, Config, State) -> + cleanup(Config), + {[{suite, Suite}, {tc, TC} | Config], State}. + +post_end_per_testcase(_, _TC, Config, Res, State) -> + cleanup(Config), + {Res, State}. + +post_end_per_suite(_, Config, State) -> + cleanup(Config), + {Config, State}. + +cleanup(Config) -> + [test_server:stop_node(N) || N <- nodes(), + not lists:member(N, proplists:get_value(nodes, Config))]. diff --git a/lib/kernel/test/loose_node.erl b/lib/kernel/test/loose_node.erl index 87a4ef01c0..cc3f9bbea0 100644 --- a/lib/kernel/test/loose_node.erl +++ b/lib/kernel/test/loose_node.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2010. All Rights Reserved. +%% Copyright Ericsson AB 2004-2016. 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/. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at %% -%% 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. +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% @@ -56,9 +57,16 @@ %% stop(Node) when is_atom(Node) -> + erlang:monitor_node(Node, true), rpc:cast(Node, erlang, halt, []), - io:format("Stopped loose node ~p~n", [Node]), - ok. + receive + {nodedown, Node} -> + io:format("Stopped loose node ~p~n", [Node]), + ok + after 10000 -> + io:format("Failed to stop loose node: ~p~n", [Node]), + {error, node_not_stopped} + end. start(Name, Args) -> start(Name, Args, -1). diff --git a/lib/kernel/test/multi_load_SUITE.erl b/lib/kernel/test/multi_load_SUITE.erl new file mode 100644 index 0000000000..f3258ea520 --- /dev/null +++ b/lib/kernel/test/multi_load_SUITE.erl @@ -0,0 +1,419 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1999-2017. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% + +-module(multi_load_SUITE). +-export([all/0,suite/0,groups/0,init_per_suite/1,end_per_suite/1, + init_per_group/2,end_per_group/2, + basic_atomic_load/1,basic_errors/1,sticky_dir/1, + on_load_failing/1,ensure_modules_loaded/1, + native_code/1]). + +-include_lib("common_test/include/ct.hrl"). +-include_lib("syntax_tools/include/merl.hrl"). + +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap,{minutes,1}}]. + +all() -> + [basic_atomic_load,basic_errors,sticky_dir,on_load_failing, + ensure_modules_loaded,native_code]. + +groups() -> + []. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + +basic_atomic_load(Config) -> + PrivDir = proplists:get_value(priv_dir, Config), + Dir = filename:join(PrivDir, multi_load_sticky_dir), + _ = file:make_dir(Dir), + + OldPath = code:get_path(), + try + code:add_patha(Dir), + do_basic(Dir) + after + code:set_path(OldPath) + end, + + ok. + +do_basic(Dir) -> + MsVer1_0 = make_modules(5, versioned_module(1)), + MsVer1 = [{M,filename:absname(F, Dir),Bin} || {M,F,Bin} <- MsVer1_0], + _ = [ok = file:write_file(F, Bin) || {_,F,Bin} <- MsVer1], + + Ms = [M || {M,_,_} <- MsVer1], + [] = [loaded || M <- Ms, is_loaded(M)], + + ok = code:atomic_load(Ms), + _ = [1 = M:M() || M <- Ms], + _ = [F = code:which(M) || {M,F,_} <- MsVer1], + [] = [not_loaded || M <- Ms, not is_loaded(M)], + + MsVer2 = update_modules(Ms, versioned_module(2)), + {ok,Prepared} = code:prepare_loading(MsVer2), + ok = code:finish_loading(Prepared), + _ = [2 = M:M() || M <- Ms], + _ = [F = code:which(M) || {M,F,_} <- MsVer2], + [] = [not_loaded || M <- Ms, not is_loaded(M)], + + MsVer3 = update_modules(Ms, versioned_module(2)), + NotPurged = lists:sort([{M,not_purged} || M <- Ms]), + NotPurged = atomic_load_error(MsVer3, true), + + ok. + +versioned_module(Ver) -> + fun(Mod) -> + ?Q(["-module('@Mod@').\n", + "-export(['@Mod@'/0]).\n", + "'@Mod@'() -> _@Ver@.\n"]) + end. + +basic_errors(_Config) -> + atomic_load_fc([42]), + atomic_load_fc([{"mod","file","bin"}]), + + finish_loading_fc(atom), + {ok,{PrepTag,_}} = code:prepare_loading([code]), + finish_loading_fc({PrepTag,[x]}), + finish_loading_fc({PrepTag,[{m,{<<>>,"",<<>>}}]}), + Prep = prepared_with_wrong_magic_bin(), + finish_loading_fc(Prep), + + [{x,badfile}] = atomic_load_error([{x,"x",<<"bad">>}], false), + [{a,badfile},{some_nonexistent_file,nofile}] = + atomic_load_error([some_nonexistent_file,{a,"a",<<>>}], + false), + + %% Modules mentioned more than once. + Mods = make_modules(2, fun basic_module/1), + Ms = [M || {M,_,_} <- Mods], + DupMods = Mods ++ [mnesia] ++ Mods ++ [mnesia], + DupErrors0 = lists:sort([mnesia|Ms]), + DupErrors = [{M,duplicated} || M <- DupErrors0], + DupErrors = atomic_load_error(DupMods, false), + + ok. + +atomic_load_fc(L) -> + {'EXIT',{function_clause,[{code,atomic_load,[L],_}|_]}} = + (catch code:atomic_load(L)), + {'EXIT',{function_clause,[{code,prepare_loading,[L],_}|_]}} = + (catch code:prepare_loading(L)). + +finish_loading_fc(Term) -> + {'EXIT',{function_clause,[{code,finish_loading,[Term],_}|_]}} = + (catch code:finish_loading(Term)). + +prepared_with_wrong_magic_bin() -> + {ok,Prep} = code:prepare_loading([?MODULE]), + prep_magic(Prep). + +prep_magic([H|T]) -> + [prep_magic(H)|prep_magic(T)]; +prep_magic(Tuple) when is_tuple(Tuple) -> + L = prep_magic(tuple_to_list(Tuple)), + list_to_tuple(L); +prep_magic(Ref) when is_reference(Ref) -> + try erlang:has_prepared_code_on_load(Ref) of + false -> + %% Create a different kind of magic ref. + ets:match_spec_compile([{'_',[true],['$_']}]) + catch + _:_ -> + Ref + end; +prep_magic(Other) -> + Other. + +sticky_dir(_Config) -> + Mod0 = make_module(lists, fun basic_module/1), + Mod1 = make_module(gen_server, fun basic_module/1), + Ms = [Mod0,Mod1], + SD = sticky_directory, + StickyErrors = [{gen_server,SD},{lists,SD}], + StickyErrors = atomic_load_error(Ms, true), + + ok. + +on_load_failing(_Config) -> + OnLoad = make_modules(1, fun on_load_module/1), + [{OnLoadMod,_,_}] = OnLoad, + Ms = make_modules(10, fun basic_module/1) ++ OnLoad, + + %% Fail because there is a module with on_load in the list. + on_load_failure(OnLoadMod, Ms), + on_load_failure(OnLoadMod, [lists:last(Ms)]), + + %% Fail because there already is a pending on_load. + [{HangingOnLoad,_,_}|_] = Ms, + spawn_hanging_on_load(HangingOnLoad), + NoOnLoadMs = lists:droplast(Ms), + {error,[{HangingOnLoad,pending_on_load}]} = + code:atomic_load(NoOnLoadMs), + hanging_on_load ! stop_hanging_and_unload, + + ok. + +on_load_failure(OnLoadMod, Ms) -> + [{OnLoadMod,on_load_not_allowed}] = atomic_load_error(Ms, false). + +spawn_hanging_on_load(Mod) -> + {Mod,Name,Bin} = make_module(Mod, "unknown", + fun(_) -> + hanging_on_load_module(Mod) + end), + spawn_link(fun() -> + {error,on_load_failure} = + code:load_binary(Mod, Name, Bin) + end). + +hanging_on_load_module(Mod) -> + ?Q(["-module('@Mod@').\n", + "-on_load(hang/0).\n", + "hang() ->\n" + " register(hanging_on_load, self()),\n" + " receive _ -> unload end.\n"]). + +ensure_modules_loaded(Config) -> + PrivDir = proplists:get_value(priv_dir, Config), + Dir = filename:join(PrivDir, multi_load_ensure_modules_loaded), + _ = file:make_dir(Dir), + + OldPath = code:get_path(), + try + code:add_patha(Dir), + do_ensure_modules_loaded(Dir) + after + code:set_path(OldPath) + end, + + ok. + +do_ensure_modules_loaded(Dir) -> + %% Create a dummy "lists" module and place it in our code path. + {lists,ListsFile,ListsCode} = make_module(lists, fun basic_module/1), + ok = file:write_file(filename:absname(ListsFile, Dir), ListsCode), + {error,sticky_directory} = code:load_file(lists), + + %% Make a new module that we can load. + Mod = make_module_file(Dir, fun basic_module/1), + false = is_loaded(Mod), + + %% Make a new module with an on_load function. + OLMod = make_module_file(Dir, fun on_load_module/1), + false = is_loaded(OLMod), + + %% lists should not be loaded again; Mod and OLMod should be + %% loaded. ?MODULE should not be reloaded, but there is no easy + %% way to test that. Repeating modules is OK. + ok = code:ensure_modules_loaded([?MODULE,lists,Mod,OLMod, + Mod,OLMod,Mod,lists]), + last = lists:last([last]), + true = is_loaded(Mod), + ok = Mod:Mod(), + true = is_loaded(OLMod), + _ = OLMod:module_info(), + + %% Unload the modules that were loaded. + [begin + code:purge(M), + code:delete(M), + code:purge(M), + false = is_loaded(M) + end || M <- [Mod,OLMod]], + + %% If there are some errors, all other modules should be loaded + %% anyway. + [{BadMod,BadFile,_}] = make_modules(1, fun basic_module/1), + ok = file:write_file(filename:absname(BadFile, Dir), <<"bad_code">>), + BadOLMod = make_module_file(Dir, fun failing_on_load_module/1), + BadEgg = bad__egg, + NativeMod = a_native_module, + NativeModFile = atom_to_list(NativeMod) ++ ".beam", + {NativeMod,_,NativeCode} = make_module(NativeMod, NativeModFile, + fun basic_module/1, [native]), + ok = file:write_file(filename:absname(NativeModFile, Dir), NativeCode), + ModulesToLoad = [OLMod,?MODULE,Mod,BadOLMod,NativeMod, + BadEgg,BadMod,lists], + {error,Error0} = code:ensure_modules_loaded(ModulesToLoad), + Error = lists:sort([{BadEgg,nofile}, + {BadMod,badfile}, + {BadOLMod,on_load_failure}]), + Error = lists:sort(Error0), + true = is_loaded(Mod), + true = is_loaded(OLMod), + true = is_loaded(NativeMod), + + ModuleNative = case erlang:system_info(hipe_architecture) of + undefined -> false; + _ -> true + end, + ModuleNative = NativeMod:module_info(native), + + ok. + +failing_on_load_module(Mod) -> + ?Q(["-module('@Mod@').\n", + "-on_load(f/0).\n", + "f() -> fail.\n"]). + +native_code(_Config) -> + case erlang:system_info(hipe_architecture) of + undefined -> + {skip,"No native support"}; + _ -> + do_native_code() + end. + +do_native_code() -> + CalledMod = native_called_module, + CallingMod = native_calling_module, + + %% Create a module in native code that calls another module. + CallingMod = make_and_load(CallingMod, + calling_module_fun(CalledMod), + [native]), + + %% Create a threaded-code module. + _ = make_and_load(CalledMod, called_module_fun(42), []), + 42 = CallingMod:call(), + + %% Now replace it with a changed module in native code. + code:purge(CalledMod), + make_and_load(CalledMod, called_module_fun(43), [native]), + true = test_server:is_native(CalledMod), + 43 = CallingMod:call(), + + %% Reload the called module and call it. + code:purge(CalledMod), + ModVer3 = make_module(CalledMod, "", called_module_fun(changed)), + ok = code:atomic_load([ModVer3]), + false = test_server:is_native(CalledMod), + changed = CallingMod:call(), + code:purge(CalledMod), + + ok. + +make_and_load(Mod, Fun, Opts) -> + {Mod,_,Code} = make_module(Mod, "", Fun, Opts), + {module,Mod} = code:load_binary(Mod, "", Code), + Mod. + +calling_module_fun(Called) -> + fun(Mod) -> + ?Q(["-module('@Mod@').\n", + "-export([call/0]).\n", + "call() -> _@Called@:f().\n"]) + end. + +called_module_fun(Ret) -> + fun(Mod) -> + ?Q(["-module('@Mod@').\n", + "-export([f/0]).\n", + "f() -> _@Ret@.\n"]) + end. + +%%% +%%% Common utilities +%%% + +atomic_load_error(Modules, ErrorInFinishLoading) -> + {error,Errors0} = code:atomic_load(Modules), + {Errors1,Bool} = + case code:prepare_loading(Modules) of + {ok,Prepared} -> + {error,Es0} = code:finish_loading(Prepared), + {Es0,true}; + {error,Es0} -> + {Es0,false} + end, + Errors = lists:sort(Errors0), + Errors = lists:sort(Errors1), + case {ErrorInFinishLoading,Bool} of + {B,B} -> + Errors; + {false,true} -> + ct:fail("code:prepare_loading/1 should have failed"); + {true,false} -> + ct:fail("code:prepare_loading/1 should have succeeded") + end. + +is_loaded(Mod) -> + case erlang:module_loaded(Mod) of + false -> + false = code:is_loaded(Mod); + true -> + {file,_} = code:is_loaded(Mod), + true + end. + +basic_module(Mod) -> + ?Q(["-module('@Mod@').\n" + "-export(['@Mod@'/0]).\n", + "'@Mod@'() -> ok."]). + +on_load_module(Mod) -> + ?Q(["-module('@Mod@').\n", + "-on_load(f/0).\n", + "f() -> ok.\n"]). + +make_module_file(Dir, Fun) -> + [{Mod,File,Code}] = make_modules(1, Fun), + ok = file:write_file(filename:absname(File, Dir), Code), + Mod. + +make_modules(0, _) -> + []; +make_modules(N, Fun) -> + U = erlang:unique_integer([positive]), + ModName = "m__" ++ integer_to_list(N) ++ "_" ++ integer_to_list(U), + Mod = list_to_atom(ModName), + ModItem = make_module(Mod, Fun), + [ModItem|make_modules(N-1, Fun)]. + +update_modules(Ms, Fun) -> + [make_module(M, Fun) || M <- Ms]. + +make_module(Mod, Fun) -> + Filename = atom_to_list(Mod) ++ ".beam", + make_module(Mod, Filename, Fun). + +make_module(Mod, Filename, Fun) -> + make_module(Mod, Filename, Fun, []). + +make_module(Mod, Filename, Fun, Opts) -> + Tree = Fun(Mod), + merl:print(Tree), + {ok,Mod,Code} = merl:compile(Tree, Opts), + {Mod,Filename,Code}. diff --git a/lib/kernel/test/myApp.erl b/lib/kernel/test/myApp.erl index 26dc74f91b..0318e55c52 100644 --- a/lib/kernel/test/myApp.erl +++ b/lib/kernel/test/myApp.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2010. All Rights Reserved. +%% Copyright Ericsson AB 1998-2016. 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/lib/kernel/test/os_SUITE.erl b/lib/kernel/test/os_SUITE.erl index 9b474c4cdf..710b9b115c 100644 --- a/lib/kernel/test/os_SUITE.erl +++ b/lib/kernel/test/os_SUITE.erl @@ -1,35 +1,46 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2013. All Rights Reserved. +%% Copyright Ericsson AB 1997-2018. 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/. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at %% -%% 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. +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% -module(os_SUITE). -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2]). --export([space_in_cwd/1, quoting/1, cmd_unicode/1, space_in_name/1, bad_command/1, - find_executable/1, unix_comment_in_command/1, deep_list_command/1, evil/1]). + init_per_group/2,end_per_group/2, + init_per_testcase/2,end_per_testcase/2]). +-export([space_in_cwd/1, quoting/1, cmd_unicode/1, + null_in_command/1, space_in_name/1, bad_command/1, + find_executable/1, unix_comment_in_command/1, deep_list_command/1, + large_output_command/1, background_command/0, background_command/1, + message_leak/1, close_stdin/0, close_stdin/1, max_size_command/1, + perf_counter_api/1]). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap,{minutes,1}}]. all() -> - [space_in_cwd, quoting, cmd_unicode, space_in_name, bad_command, - find_executable, unix_comment_in_command, deep_list_command, evil]. + [space_in_cwd, quoting, cmd_unicode, null_in_command, + space_in_name, bad_command, + find_executable, unix_comment_in_command, deep_list_command, + large_output_command, background_command, message_leak, + close_stdin, max_size_command, perf_counter_api]. groups() -> []. @@ -46,16 +57,27 @@ init_per_group(_GroupName, Config) -> end_per_group(_GroupName, Config) -> Config. +init_per_testcase(TC, Config) + when TC =:= background_command; TC =:= close_stdin -> + case os:type() of + {win32, _} -> + {skip,"Should not work on windows"}; + _ -> + Config + end; +init_per_testcase(_TC,Config) -> + Config. -space_in_cwd(doc) -> - "Test that executing a command in a current working directory " - "with space in its name works."; -space_in_cwd(suite) -> []; +end_per_testcase(_,_Config) -> + ok. + +%% Test that executing a command in a current working directory +%% with space in its name works. space_in_cwd(Config) when is_list(Config) -> - ?line PrivDir = ?config(priv_dir, Config), - ?line Dirname = filename:join(PrivDir, "cwd with space"), - ?line ok = file:make_dir(Dirname), - ?line ok = file:set_cwd(Dirname), + PrivDir = proplists:get_value(priv_dir, Config), + Dirname = filename:join(PrivDir, "cwd with space"), + ok = file:make_dir(Dirname), + ok = file:set_cwd(Dirname), %% Using `more' gives the almost the same result on both Unix and Windows. @@ -66,70 +88,74 @@ space_in_cwd(Config) when is_list(Config) -> "more </dev/null" end, - ?line case os:cmd(Cmd) of - [] -> ok; % Unix. - "\r\n" -> ok; % Windows. - Other -> - ?line test_server:fail({unexpected, Other}) - end, + case os:cmd(Cmd) of + [] -> ok; % Unix. + "\r\n" -> ok; % Windows. + Other -> + ct:fail({unexpected, Other}) + end, - ?t:sleep(5), - ?line [] = receive_all(), + ct:sleep(5), + [] = receive_all(), ok. -quoting(doc) -> "Test that various ways of quoting arguments work."; -quoting(suite) -> []; +%% Test that various ways of quoting arguments work. quoting(Config) when is_list(Config) -> - ?line DataDir = ?config(data_dir, Config), - ?line Echo = filename:join(DataDir, "my_echo"), - - ?line comp("one", os:cmd(Echo ++ " one")), - ?line comp("one::two", os:cmd(Echo ++ " one two")), - ?line comp("one two", os:cmd(Echo ++ " \"one two\"")), - ?line comp("x::one two::y", os:cmd(Echo ++ " x \"one two\" y")), - ?line comp("x::one two", os:cmd(Echo ++ " x \"one two\"")), - ?line comp("one two::y", os:cmd(Echo ++ " \"one two\" y")), - ?line comp("x::::y", os:cmd(Echo ++ " x \"\" y")), - ?t:sleep(5), - ?line [] = receive_all(), + DataDir = proplists:get_value(data_dir, Config), + Echo = filename:join(DataDir, "my_echo"), + + comp("one", os:cmd(Echo ++ " one")), + comp("one::two", os:cmd(Echo ++ " one two")), + comp("one two", os:cmd(Echo ++ " \"one two\"")), + comp("x::one two::y", os:cmd(Echo ++ " x \"one two\" y")), + comp("x::one two", os:cmd(Echo ++ " x \"one two\"")), + comp("one two::y", os:cmd(Echo ++ " \"one two\" y")), + comp("x::::y", os:cmd(Echo ++ " x \"\" y")), + ct:sleep(5), + [] = receive_all(), ok. -cmd_unicode(doc) -> "Test that unicode arguments work."; -cmd_unicode(suite) -> []; +%% Test that unicode arguments work. cmd_unicode(Config) when is_list(Config) -> - ?line DataDir = ?config(data_dir, Config), - ?line Echo = filename:join(DataDir, "my_echo"), - - ?line comp("one", os:cmd(Echo ++ " one")), - ?line comp("one::two", os:cmd(Echo ++ " one two")), - ?line comp("åäö::ϼΩ", os:cmd(Echo ++ " åäö " ++ [1020, 937])), - ?t:sleep(5), - ?line [] = receive_all(), + DataDir = proplists:get_value(data_dir, Config), + Echo = filename:join(DataDir, "my_echo"), + + comp("one", os:cmd(Echo ++ " one")), + comp("one::two", os:cmd(Echo ++ " one two")), + comp("åäö::ϼΩ", os:cmd(Echo ++ " åäö " ++ [1020, 937])), + ct:sleep(5), + [] = receive_all(), ok. +null_in_command(Config) -> + {Ok, Error} = case os:type() of + {win32,_} -> {"dir", "di\0r"}; + _ -> {"ls", "l\0s"} + end, + true = is_list(try os:cmd(Ok) catch Class0:_ -> Class0 end), + error = try os:cmd(Error) catch Class1:_ -> Class1 end, + ok. -space_in_name(doc) -> - "Test that program with a space in its name can be executed."; -space_in_name(suite) -> []; +%% Test that program with a space in its name can be executed. space_in_name(Config) when is_list(Config) -> - ?line PrivDir = ?config(priv_dir, Config), - ?line DataDir = ?config(data_dir, Config), - ?line Spacedir = filename:join(PrivDir, "program files"), + PrivDir = proplists:get_value(priv_dir, Config), + DataDir = proplists:get_value(data_dir, Config), + Spacedir = filename:join(PrivDir, "program files"), Ext = case os:type() of {win32,_} -> ".exe"; _ -> "" end, - ?line OrigEcho = filename:join(DataDir, "my_echo" ++ Ext), - ?line Echo0 = filename:join(Spacedir, "my_echo" ++ Ext), + OrigEcho = filename:join(DataDir, "my_echo" ++ Ext), + Echo0 = filename:join(Spacedir, "my_echo" ++ Ext), %% Copy the `my_echo' program to a directory whose name contains a space. - ?line ok = file:make_dir(Spacedir), - ?line {ok, Bin} = file:read_file(OrigEcho), - ?line ok = file:write_file(Echo0, Bin), - ?line Echo = filename:nativename(Echo0), - ?line ok = file:change_mode(Echo, 8#777), % Make it executable on Unix. + ok = file:make_dir(Spacedir), + {ok, Bin} = file:read_file(OrigEcho), + ok = file:write_file(Echo0, Bin), + Echo = filename:nativename(Echo0), + ok = file:change_mode(Echo, 8#777), % Make it executable on Unix. %% Run the echo program. %% Quoting on windows depends on if the full path of the executable @@ -145,78 +171,74 @@ space_in_name(Config) when is_list(Config) -> _ -> "\"" end, - ?line comp("", os:cmd(Quote ++ Echo ++ Quote)), - ?line comp("a::b::c", os:cmd(Quote ++ Echo ++ Quote ++ " a b c")), - ?t:sleep(5), - ?line [] = receive_all(), + comp("", os:cmd(Quote ++ Echo ++ Quote)), + comp("a::b::c", os:cmd(Quote ++ Echo ++ Quote ++ " a b c")), + ct:sleep(5), + [] = receive_all(), ok. -bad_command(doc) -> - "Check that a bad command doesn't crasch the server or the emulator (it used to)."; -bad_command(suite) -> []; +%% Check that a bad command doesn't crasch the server or the emulator (it used to). bad_command(Config) when is_list(Config) -> - ?line catch os:cmd([a|b]), - ?line catch os:cmd({bad, thing}), + catch os:cmd([a|b]), + catch os:cmd({bad, thing}), %% This should at least not crash (on Unix it typically returns %% a message from the shell). - ?line os:cmd("xxxxx"), + os:cmd("xxxxx"), ok. -find_executable(suite) -> []; -find_executable(doc) -> []; find_executable(Config) when is_list(Config) -> case os:type() of {win32, _} -> - ?line DataDir = filename:join(?config(data_dir, Config), "win32"), - ?line ok = file:set_cwd(filename:join([DataDir, "current"])), - ?line Bin = filename:join(DataDir, "bin"), - ?line Abin = filename:join(DataDir, "abin"), - ?line UsrBin = filename:join([DataDir, "usr", "bin"]), - ?line {ok, Current} = file:get_cwd(), + DataDir = filename:join(proplists:get_value(data_dir, Config), "win32"), + ok = file:set_cwd(filename:join([DataDir, "current"])), + Bin = filename:join(DataDir, "bin"), + Abin = filename:join(DataDir, "abin"), + UsrBin = filename:join([DataDir, "usr", "bin"]), + {ok, Current} = file:get_cwd(), - ?line Path = lists:concat([Bin, ";", Abin, ";", UsrBin]), - ?line io:format("Path = ~s", [Path]), + Path = lists:concat([Bin, ";", Abin, ";", UsrBin]), + io:format("Path = ~s", [Path]), %% Search for programs in Bin (second element in PATH). - ?line find_exe(Abin, "my_ar", ".exe", Path), - ?line find_exe(Abin, "my_ascii", ".com", Path), - ?line find_exe(Abin, "my_adb", ".bat", Path), + find_exe(Abin, "my_ar", ".exe", Path), + find_exe(Abin, "my_ascii", ".com", Path), + find_exe(Abin, "my_adb", ".bat", Path), %% OTP-3626 find names of executables given with extension - ?line find_exe(Abin, "my_ar.exe", "", Path), - ?line find_exe(Abin, "my_ascii.com", "", Path), - ?line find_exe(Abin, "my_adb.bat", "", Path), - ?line find_exe(Abin, "my_ar.EXE", "", Path), - ?line find_exe(Abin, "my_ascii.COM", "", Path), - ?line find_exe(Abin, "MY_ADB.BAT", "", Path), + find_exe(Abin, "my_ar.exe", "", Path), + find_exe(Abin, "my_ascii.com", "", Path), + find_exe(Abin, "my_adb.bat", "", Path), + find_exe(Abin, "my_ar.EXE", "", Path), + find_exe(Abin, "my_ascii.COM", "", Path), + find_exe(Abin, "MY_ADB.BAT", "", Path), %% Search for programs in Abin (second element in PATH). - ?line find_exe(Abin, "my_ar", ".exe", Path), - ?line find_exe(Abin, "my_ascii", ".com", Path), - ?line find_exe(Abin, "my_adb", ".bat", Path), + find_exe(Abin, "my_ar", ".exe", Path), + find_exe(Abin, "my_ascii", ".com", Path), + find_exe(Abin, "my_adb", ".bat", Path), %% Search for programs in the current working directory. - ?line find_exe(Current, "my_program", ".exe", Path), - ?line find_exe(Current, "my_command", ".com", Path), - ?line find_exe(Current, "my_batch", ".bat", Path), + find_exe(Current, "my_program", ".exe", Path), + find_exe(Current, "my_command", ".com", Path), + find_exe(Current, "my_batch", ".bat", Path), ok; {unix, _} -> - DataDir = ?config(data_dir, Config), + DataDir = proplists:get_value(data_dir, Config), %% Smoke test. - case lib:progname() of - erl -> - ?line ErlPath = os:find_executable("erl"), - ?line true = is_list(ErlPath), - ?line true = filelib:is_regular(ErlPath); + case ct:get_progname() of + "erl" -> + ErlPath = os:find_executable("erl"), + true = is_list(ErlPath), + true = filelib:is_regular(ErlPath); _ -> %% Don't bother -- the progname could include options. ok end, %% Never return a directory name. - ?line false = os:find_executable("unix", [DataDir]), + false = os:find_executable("unix", [DataDir]), ok end. @@ -232,29 +254,23 @@ find_exe(Where, Name, Ext, Path) -> Other -> io:format("Expected ~p; got (converted to absolute) ~p", [Expected, Other]), - test_server:fail() + ct:fail(failed) end; Other -> io:format("Expected ~p; got ~p", [Expected, Other]), - test_server:fail() + ct:fail(failed) end. -unix_comment_in_command(doc) -> - "OTP-1805: Test that os:cmd(\"ls #\") works correctly (used to hang)."; -unix_comment_in_command(suite) -> []; +%% OTP-1805: Test that os:cmd(\ls #\) works correctly (used to hang). unix_comment_in_command(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(20)), - ?line Priv = ?config(priv_dir, Config), - ?line ok = file:set_cwd(Priv), - ?line _ = os:cmd("ls #"), % Any result is ok. - ?t:sleep(5), - ?line [] = receive_all(), - ?line test_server:timetrap_cancel(Dog), + Priv = proplists:get_value(priv_dir, Config), + ok = file:set_cwd(Priv), + _ = os:cmd("ls #"), % Any result is ok. + ct:sleep(5), + [] = receive_all(), ok. -deep_list_command(doc) -> - "Check that a deep list in command works equally on unix and on windows."; -deep_list_command(suite) -> []; +%% Check that a deep list in command works equally on unix and on windows. deep_list_command(Config) when is_list(Config) -> %% As a 'io_lib' module description says: "There is no guarantee that the %% character lists returned from some of the functions are flat, they can @@ -266,50 +282,101 @@ deep_list_command(Config) when is_list(Config) -> %% FYI: [$e, $c, "ho"] =:= io_lib:format("ec~s", ["ho"]) ok. +%% Test to make sure that the correct data is +%% received when doing large commands. +large_output_command(Config) when is_list(Config) -> + %% Maximum allowed on windows is 8192, so we test well below that + AAA = lists:duplicate(7000, $a), + comp(AAA,os:cmd("echo " ++ AAA)). --define(EVIL_PROCS, 100). --define(EVIL_LOOPS, 100). --define(PORT_CREATOR, os_cmd_port_creator). -evil(Config) when is_list(Config) -> - Dog = test_server:timetrap(test_server:minutes(5)), - Parent = self(), - Ps = lists:map(fun (N) -> - spawn_link(fun () -> - evil_loop(Parent, ?EVIL_LOOPS,N) - end) - end, lists:seq(1, ?EVIL_PROCS)), - Devil = spawn_link(fun () -> devil(hd(Ps), hd(lists:reverse(Ps))) end), - lists:foreach(fun (P) -> receive {P, done} -> ok end end, Ps), - unlink(Devil), - exit(Devil, kill), - test_server:timetrap_cancel(Dog), - ok. +%% Test that it is possible on unix to start a background task using os:cmd. +background_command() -> + [{timetrap, {seconds, 5}}]. +background_command(_Config) -> + %% This testcase fails when the os:cmd takes + %% longer then the 5 second timeout + os:cmd("sleep 10&"). + +%% Test that message does not leak to the calling process +message_leak(_Config) -> + process_flag(trap_exit, true), + + os:cmd("echo hello"), + [] = receive_all(), + + case os:type() of + {unix, _} -> + os:cmd("for i in $(seq 1 100); do echo hello; done&"), + [] = receive_all(); + _ -> + ok % Cannot background on non-unix + end, + + process_flag(trap_exit, false). + +%% Test that os:cmd closes stdin of the program that is executed +close_stdin() -> + [{timetrap, {seconds, 5}}]. +close_stdin(Config) -> + DataDir = proplists:get_value(data_dir, Config), + Fds = filename:join(DataDir, "my_fds"), + + "-1" = os:cmd(Fds). + +max_size_command(_Config) -> + + Res20 = os:cmd("cat /dev/zero", #{ max_size => 20 }), + 20 = length(Res20), + + Res0 = os:cmd("cat /dev/zero", #{ max_size => 0 }), + 0 = length(Res0), + + Res32768 = os:cmd("cat /dev/zero", #{ max_size => 32768 }), + 32768 = length(Res32768), + + ResHello = string:trim(os:cmd("echo hello", #{ max_size => 20 })), + 5 = length(ResHello). + +%% Test that the os:perf_counter api works as expected +perf_counter_api(_Config) -> + + true = is_integer(os:perf_counter()), + true = os:perf_counter() > 0, + + Conv = fun(T1, T2) -> + erlang:convert_time_unit(T2 - T1, perf_counter, nanosecond) + end, + + do_perf_counter_test([], Conv, 120000000, 80000000), + do_perf_counter_test([1000], fun(T1, T2) -> T2 - T1 end, 120, 80). + +do_perf_counter_test(CntArgs, Conv, Upper, Lower) -> + %% We run the test multiple times to try to get a somewhat + %% stable value... what does this test? That the + %% calculate_perf_counter_unit in sys_time.c works somewhat ok. + do_perf_counter_test(CntArgs, Conv, Upper, Lower, 10). + +do_perf_counter_test(CntArgs, _Conv, Upper, Lower, 0) -> + ct:fail("perf_counter_test ~p ~p ~p",[CntArgs, Upper, Lower]); +do_perf_counter_test(CntArgs, Conv, Upper, Lower, Iters) -> + + T1 = apply(os, perf_counter, CntArgs), + timer:sleep(100), + T2 = apply(os, perf_counter, CntArgs), + TsDiff = Conv(T1, T2), + ct:log("T1: ~p~n" + "T2: ~p~n" + "TsDiff: ~p~n", + [T1,T2,TsDiff]), + + if + TsDiff < Upper, TsDiff > Lower -> + ok; + true -> + do_perf_counter_test(CntArgs, Conv, Upper, Lower, Iters-1) + end. -devil(P1, P2) -> - erlang:display({?PORT_CREATOR, whereis(?PORT_CREATOR)}), - (catch ?PORT_CREATOR ! lists:seq(1,1000000)), - (catch ?PORT_CREATOR ! lists:seq(1,666)), - (catch ?PORT_CREATOR ! grrrrrrrrrrrrrrrr), - (catch ?PORT_CREATOR ! {'EXIT', P1, buhuuu}), - (catch ?PORT_CREATOR ! {'EXIT', hd(erlang:ports()), buhuuu}), - (catch ?PORT_CREATOR ! {'EXIT', P2, arggggggg}), - receive after 500 -> ok end, - (catch exit(whereis(?PORT_CREATOR), kill)), - (catch ?PORT_CREATOR ! ">8|"), - receive after 500 -> ok end, - (catch exit(whereis(?PORT_CREATOR), diiiiiiiiiiiiiiiiiiiie)), - receive after 100 -> ok end, - devil(P1, P2). - -evil_loop(Parent, Loops, N) -> - Res = integer_to_list(N), - evil_loop(Parent, Loops, Res, "echo " ++ Res). - -evil_loop(Parent, 0, _Res, _Cmd) -> - Parent ! {self(), done}; -evil_loop(Parent, Loops, Res, Cmd) -> - comp(Res, os:cmd(Cmd)), - evil_loop(Parent, Loops-1, Res, Cmd). +%% Util functions comp(Expected, Got) -> case strip_nl(Got) of @@ -318,10 +385,10 @@ comp(Expected, Got) -> Other -> ok = io:format("Expected: ~ts\n", [Expected]), ok = io:format("Got: ~ts\n", [Other]), - test_server:fail() + ct:fail(failed) end. -%% Like lib:nonl/1, but strips \r as well as \n. +%% strips \n and \r\n from end of string strip_nl([$\r, $\n]) -> []; strip_nl([$\n]) -> []; diff --git a/lib/kernel/test/os_SUITE_data/Makefile.src b/lib/kernel/test/os_SUITE_data/Makefile.src index 912d0cbcb1..f83f781411 100644 --- a/lib/kernel/test/os_SUITE_data/Makefile.src +++ b/lib/kernel/test/os_SUITE_data/Makefile.src @@ -3,7 +3,7 @@ LD = @LD@ CFLAGS = @CFLAGS@ -I@erl_include@ @DEFS@ CROSSLDFLAGS = @CROSSLDFLAGS@ -PROGS = my_echo@exe@ +PROGS = my_echo@exe@ my_fds@exe@ all: $(PROGS) @@ -12,3 +12,9 @@ my_echo@exe@: my_echo@obj@ my_echo@obj@: my_echo.c $(CC) -c -o my_echo@obj@ $(CFLAGS) my_echo.c + +my_fds@exe@: my_fds@obj@ + $(LD) $(CROSSLDFLAGS) -o my_fds my_fds@obj@ @LIBS@ + +my_fds@obj@: my_fds.c + $(CC) -c -o my_fds@obj@ $(CFLAGS) my_fds.c diff --git a/lib/kernel/test/os_SUITE_data/my_fds.c b/lib/kernel/test/os_SUITE_data/my_fds.c new file mode 100644 index 0000000000..704a4d1e1d --- /dev/null +++ b/lib/kernel/test/os_SUITE_data/my_fds.c @@ -0,0 +1,9 @@ +#include <stdio.h> + +int +main(int argc, char** argv) +{ + char buff[1]; + int res = read(stdin, buff, 1); + printf("%d", res); +} diff --git a/lib/kernel/test/pdict_SUITE.erl b/lib/kernel/test/pdict_SUITE.erl index 98cff0222e..3685e51c10 100644 --- a/lib/kernel/test/pdict_SUITE.erl +++ b/lib/kernel/test/pdict_SUITE.erl @@ -1,26 +1,26 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2013. All Rights Reserved. +%% Copyright Ericsson AB 1999-2018. 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/. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at %% -%% 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. +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% -module(pdict_SUITE). -%% NB: The ?line macro cannot be used when testing the dictionary. --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -define(M(A,B),m(A,B,?MODULE,?LINE)). -ifdef(DEBUG). @@ -31,22 +31,30 @@ -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, - simple/1, complicated/1, heavy/1, info/1]). + mixed/1, + literals/1, + destructive/1, + simple/1, complicated/1, heavy/1, simple_all_keys/1, info/1]). -export([init_per_testcase/2, end_per_testcase/2]). -export([other_process/2]). +-export([put_do/2, get_do/1, erase_do/1]). + init_per_testcase(_Case, Config) -> - ?line Dog = ?t:timetrap(test_server:minutes(10)), - [{watchdog, Dog} | Config]. -end_per_testcase(_Case, Config) -> - Dog = ?config(watchdog, Config), - test_server:timetrap_cancel(Dog), + Config. + +end_per_testcase(_Case, _Config) -> ok. -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap,{minutes,1}}]. all() -> - [simple, complicated, heavy, info]. + [simple, complicated, heavy, simple_all_keys, info, + literals, + destructive, + mixed]. groups() -> []. @@ -64,12 +72,10 @@ end_per_group(_GroupName, Config) -> Config. -simple(doc) -> - ["Tests simple functionality in process dictionary."]; -simple(suite) -> - []; +%% Tests simple functionality in process dictionary. simple(Config) when is_list(Config) -> XX = get(), + ok = match_keys(XX), erase(), L = [a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p, q,r,s,t,u,v,x,y,z,'A','B','C','D'], @@ -105,16 +111,19 @@ simple(Config) when is_list(Config) -> complicated(Config) when is_list(Config) -> Previous = get(), + ok = match_keys(Previous), Previous = erase(), - N = case ?t:is_debug() of + N = case test_server:is_debug() of false -> 500000; true -> 5000 end, comp_1(N), comp_2(N), N = comp_3(lists:sort(get()), 1), + ok = match_keys(get()), comp_4(get()), [] = get(), + [] = get_keys(), [put(Key, Value) || {Key,Value} <- Previous], ok. @@ -138,10 +147,7 @@ comp_4([{{key,_}=K,{value,_}=Val}|T]) -> comp_4(T); comp_4([]) -> ok. -heavy(doc) -> - ["Tests heavy usage of the process dictionary"]; -heavy(suite) -> - []; +%% Tests heavy usage of the process dictionary. heavy(Config) when is_list(Config) -> XX = get(), erase(), @@ -151,7 +157,7 @@ heavy(Config) when is_list(Config) -> ?M([],get()), time(5000), ?M([],get()), - case {os:type(),?t:is_debug()} of + case {os:type(),test_server:is_debug()} of {_,true} -> ok; _ -> time(50000), @@ -160,10 +166,27 @@ heavy(Config) when is_list(Config) -> [put(Key, Value) || {Key,Value} <- XX], ok. -info(doc) -> - ["Tests process_info(Pid, dictionary)"]; -info(suite) -> - []; +simple_all_keys(Config) when is_list(Config) -> + erase(), + ok = simple_all_keys_add_loop(1000), + [] = get_keys(), + [] = get(), + ok. + +simple_all_keys_add_loop(0) -> + simple_all_keys_del_loop(erlang:get_keys()); +simple_all_keys_add_loop(N) -> + put(gen_key(N),value), + ok = match_keys(get()), + simple_all_keys_add_loop(N-1). + +simple_all_keys_del_loop([]) -> ok; +simple_all_keys_del_loop([K|Ks]) -> + value = erase(K), + ok = match_keys(get()), + simple_all_keys_del_loop(Ks). + +%% Tests process_info(Pid, dictionary). info(Config) when is_list(Config) -> L = [a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p, q,r,s,t,u,v,x,y,z,'A','B','C','D'], @@ -339,3 +362,151 @@ m(A,B,Module,Line) -> [A,B,Module,Line]), exit({no_match,{A,B},Module,Line}) end. + +match_keys(All) -> + Ks = lists:sort([K||{K,_}<-All]), + Ks = lists:sort(erlang:get_keys()), + ok. + + +%% Test destructive put optimization of immed values +%% does not affect get/0 or process_info. +destructive(_Config) -> + Keys = lists:seq(1,100), + [put(Key, 17) || Key <- Keys], + Get1 = get(), + {dictionary,PI1} = process_info(self(), dictionary), + + [begin + {Key, 17} = lists:keyfind(Key, 1, Get1), + {Key, 17} = lists:keyfind(Key, 1, PI1) + end + || Key <- Keys], + + [17 = put(Key, 42) || Key <- Keys], % Mutate + + Get2 = get(), + {dictionary,PI2} = process_info(self(), dictionary), + + [begin + {Key, 17} = lists:keyfind(Key, 1, Get1), + {Key, 17} = lists:keyfind(Key, 1, PI1), + {Key, 42} = lists:keyfind(Key, 1, Get2), + {Key, 42} = lists:keyfind(Key, 1, PI2) + + end + || Key <- Keys], + + ok. + +%% Do random mixed put/erase to test grow/shrink +%% Written for a temporary bug in gc during shrink +mixed(_Config) -> + Rand0 = rand:seed_s(exsplus), + io:format("Random seed = ~p\n\n", [rand:export_seed_s(Rand0)]), + + erts_debug:set_internal_state(available_internal_state, true), + try + C = do_mixed([10,0,100,50,1000,500,600,100,150,1,11,2,30,0], + 0, + array:new(), + 1, + Rand0), + io:format("\nDid total of ~p operations\n", [C]) + after + erts_debug:set_internal_state(available_internal_state, false) + end. + +do_mixed([], _, _, C, _) -> + C; +do_mixed([GoalN | Tail], GoalN, Array, C, Rand0) -> + io:format("Reached goal of ~p keys in dict after ~p mixed ops\n",[GoalN, C]), + GoalN = array:size(Array), + do_mixed(Tail, GoalN, Array, C, Rand0); +do_mixed([GoalN | _]=Goals, CurrN, Array0, C, Rand0) -> + CurrN = array:size(Array0), + GrowPercent = case GoalN > CurrN of + true when CurrN == 0 -> 100; + true -> 75; + false -> 25 + end, + {R, Rand1} = rand:uniform_s(100, Rand0), + case R of + _ when R =< GrowPercent -> %%%%%%%%%%%%% GROW + {Key, Rand2} = rand:uniform_s(10000, Rand1), + case put(Key, {Key,C}) of + undefined -> + Array1 = array:set(CurrN, Key, Array0), + do_mixed(Goals, CurrN+1, Array1, C+1, Rand2); + _ -> + do_mixed(Goals, CurrN, Array0, C+1, Rand2) + end; + + _ -> %%%%%%%%%% SHRINK + {Kix, Rand2} = rand:uniform_s(CurrN, Rand1), + Key = array:get(Kix-1, Array0), + + %% provoke GC during shrink + erts_debug:set_internal_state(fill_heap, true), + + {Key, _} = erase(Key), + Array1 = array:set(Kix-1, array:get(CurrN-1, Array0), Array0), + Array2 = array:resize(CurrN-1, Array1), + do_mixed(Goals, CurrN-1, Array2, C+1, Rand2) + end. + +%% Test hash precalculation of literal keys +literals(_Config) -> + %% Put literal -> get variable + put(1742, "1742"), + "1742" = ?MODULE:get_do(1742), + "1742" = ?MODULE:erase_do(1742), + + put(-1742, "-1742"), + "-1742" = ?MODULE:get_do(-1742), + "-1742" = ?MODULE:erase_do(-1742), + + put([], "NIL"), + "NIL" = ?MODULE:get_do([]), + "NIL" = ?MODULE:erase_do([]), + + put(<<"binary">>, "binary"), + "binary" = ?MODULE:get_do(<<"binary">>), + "binary" = ?MODULE:erase_do(<<"binary">>), + + BigBin = <<"A large binary with a lot of bytes to make it go off heap as shared and reference counted">>, + put(BigBin, "bigbin"), + "bigbin" = ?MODULE:get_do(BigBin), + "bigbin" = ?MODULE:erase_do(BigBin), + + %% Put variable -> get literal + ?MODULE:put_do(4217, "4217"), + "4217" = get(4217), + "4217" = erase(4217), + + ?MODULE:put_do(-4217, "-4217"), + "-4217" = get(-4217), + "-4217" = erase(-4217), + + ?MODULE:put_do([], "NIL"), + "NIL" = get([]), + "NIL" = erase([]), + + ?MODULE:put_do(<<"bytes">>, "bytes"), + "bytes" = get(<<"bytes">>), + "bytes" = erase(<<"bytes">>), + + ?MODULE:put_do(BigBin, "BigBin"), + "BigBin" = get(BigBin), + "BigBin" = erase(BigBin), + + ok. + +put_do(K, V) -> + put(K, V). + +get_do(K) -> + get(K). + +erase_do(K) -> + erase(K). diff --git a/lib/kernel/test/pg2_SUITE.erl b/lib/kernel/test/pg2_SUITE.erl index 520b53b4e4..acecd34ead 100644 --- a/lib/kernel/test/pg2_SUITE.erl +++ b/lib/kernel/test/pg2_SUITE.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2011. All Rights Reserved. +%% Copyright Ericsson AB 2008-2017. 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/. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at %% -%% 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. +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %%---------------------------------------------------------------- @@ -20,9 +21,9 @@ %%----------------------------------------------------------------- -module(pg2_SUITE). --include_lib("test_server/include/test_server.hrl"). --define(datadir, ?config(data_dir, Config)). --define(privdir, ?config(priv_dir, Config)). +-include_lib("common_test/include/ct.hrl"). +-define(datadir, proplists:get_value(data_dir, Config)). +-define(privdir, proplists:get_value(priv_dir, Config)). -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, @@ -30,36 +31,32 @@ -export([ otp_7277/1, otp_8259/1, otp_8653/1, - compat/1, basic/1]). - -% Default timetrap timeout (set in init_per_testcase). --define(default_timeout, ?t:minutes(1)). + basic/1]). -define(TESTCASE, testcase_name). --define(testcase, ?config(?TESTCASE, Config)). +-define(testcase, proplists:get_value(?TESTCASE, Config)). %% Internal export. -export([mk_part_node_and_group/3, part2/4, mk_part_node/3, part1/5, p_init/3, start_proc/1, sane/0]). init_per_testcase(Case, Config) -> - ?line Dog = ?t:timetrap(?default_timeout), - [{?TESTCASE, Case}, {watchdog, Dog} | Config]. + [{?TESTCASE, Case}| Config]. end_per_testcase(_Case, _Config) -> test_server_ctrl:kill_slavenodes(), - Dog = ?config(watchdog, _Config), - test_server:timetrap_cancel(Dog), ok. -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap,{minutes,1}}]. all() -> [{group, tickets}]. groups() -> [{tickets, [], - [otp_7277, otp_8259, otp_8653, compat, basic]}]. + [otp_7277, otp_8259, otp_8653, basic]}]. init_per_suite(Config) -> Config. @@ -75,67 +72,59 @@ end_per_group(_GroupName, Config) -> -otp_7277(doc) -> - "OTP-7277. Bugfix leave()."; -otp_7277(suite) -> []; +%% OTP-7277. Bugfix leave(). otp_7277(Config) when is_list(Config) -> - ?line ok = pg2:create(a), - ?line ok = pg2:create(b), + ok = pg2:create(a), + ok = pg2:create(b), P = spawn(forever()), - ?line ok = pg2:join(a, P), - ?line ok = pg2:leave(b, P), - ?line true = exit(P, kill), + ok = pg2:join(a, P), + ok = pg2:leave(b, P), + true = exit(P, kill), case {pg2:get_members(a), pg2:get_local_members(a)} of {[], []} -> ok; _ -> timer:sleep(100), - ?line [] = pg2:get_members(a), - ?line [] = pg2:get_local_members(a) + [] = pg2:get_members(a), + [] = pg2:get_local_members(a) end, - ?line _ = pg2:delete(a), - ?line _ = pg2:delete(b), + _ = pg2:delete(a), + _ = pg2:delete(b), ok. -define(UNTIL(Seq), loop_until_true(fun() -> Seq end, Config)). -define(UNTIL_LOOP, 300). -otp_8653(suite) -> []; -otp_8653(doc) -> - ["OTP-8259. Member was not removed after being killed."]; +%% OTP-8259. Member was not removed after being killed. otp_8653(Config) when is_list(Config) -> - Timeout = 15, - ?line Dog = test_server:timetrap({seconds,Timeout}), - - ?line [A, B, C] = start_nodes([a, b, c], peer, Config), + [A, B, C] = start_nodes([a, b, c], peer, Config), - ?line wait_for_ready_net(Config), + wait_for_ready_net(Config), - % make b and c connected, partitioned from node() and a - ?line rpc_cast(B, ?MODULE, part2, [Config, node(), A, C]), - ?line ?UNTIL(is_ready_partition(Config)), + %% make b and c connected, partitioned from node() and a + rpc_cast(B, ?MODULE, part2, [Config, node(), A, C]), + ?UNTIL(is_ready_partition(Config)), - % Connect to the other partition. - ?line pong = net_adm:ping(B), + %% Connect to the other partition. + pong = net_adm:ping(B), timer:sleep(100), - ?line pong = net_adm:ping(C), - ?line _ = global:sync(), - ?line [A, B, C] = lists:sort(nodes()), + pong = net_adm:ping(C), + _ = global:sync(), + [A, B, C] = lists:sort(nodes()), G = pg2_otp_8653, - ?line ?UNTIL(begin - GA = lists:sort(rpc:call(A, pg2, get_members, [G])), - GB = lists:sort(rpc:call(B, pg2, get_members, [G])), - GC = lists:sort(rpc:call(C, pg2, get_members, [G])), - GT = lists:sort(pg2:get_members(G)), - GA =:= GB andalso - GB =:= GC andalso - GC =:= GT andalso - 8 =:= length(GA) - end), - ?line ok = pg2:delete(G), - ?line stop_nodes([A,B,C]), - ?line test_server:timetrap_cancel(Dog), + ?UNTIL(begin + GA = lists:sort(rpc:call(A, pg2, get_members, [G])), + GB = lists:sort(rpc:call(B, pg2, get_members, [G])), + GC = lists:sort(rpc:call(C, pg2, get_members, [G])), + GT = lists:sort(pg2:get_members(G)), + GA =:= GB andalso + GB =:= GC andalso + GC =:= GT andalso + 8 =:= length(GA) + end), + ok = pg2:delete(G), + stop_nodes([A,B,C]), ok. part2(Config, Main, A, C) -> @@ -157,54 +146,48 @@ mk_part_node_and_group(File, MyPart0, Config) -> _ = [ok = pg2:join(G, Pid) || _ <- [1,1]], touch(File, "done"). -otp_8259(suite) -> []; -otp_8259(doc) -> - ["OTP-8259. Member was not removed after being killed."]; +%% OTP-8259. Member was not removed after being killed. otp_8259(Config) when is_list(Config) -> - Timeout = 15, - ?line Dog = test_server:timetrap({seconds,Timeout}), - - ?line [A, B, C] = start_nodes([a, b, c], peer, Config), + [A, B, C] = start_nodes([a, b, c], peer, Config), - ?line wait_for_ready_net(Config), + wait_for_ready_net(Config), G = pg2_otp_8259, Name = otp_8259_a_global_name, - % start different processes in both partitions - ?line {Pid, yes} = rpc:call(A, ?MODULE, start_proc, [Name]), + %% start different processes in both partitions + {Pid, yes} = rpc:call(A, ?MODULE, start_proc, [Name]), - ?line ok = pg2:create(G), - ?line ok = pg2:join(G, Pid), + ok = pg2:create(G), + ok = pg2:join(G, Pid), - % make b and c connected, partitioned from node() and a - ?line rpc_cast(B, ?MODULE, part1, [Config, node(), A, C, Name]), - ?line ?UNTIL(is_ready_partition(Config)), + %% make b and c connected, partitioned from node() and a + rpc_cast(B, ?MODULE, part1, [Config, node(), A, C, Name]), + ?UNTIL(is_ready_partition(Config)), - % Connect to the other partition. - % The resolver on node b will be called. - ?line pong = net_adm:ping(B), + %% Connect to the other partition. + %% The resolver on node b will be called. + pong = net_adm:ping(B), timer:sleep(100), - ?line pong = net_adm:ping(C), - ?line _ = global:sync(), - ?line [A, B, C] = lists:sort(nodes()), + pong = net_adm:ping(C), + _ = global:sync(), + [A, B, C] = lists:sort(nodes()), %% Pid has been killed by the resolver. %% Pid has been removed from pg2 on all nodes, in particular node B. - ?line ?UNTIL([] =:= rpc:call(B, pg2, get_members, [G])), - ?line ?UNTIL([] =:= pg2:get_members(G)), - ?line ?UNTIL([] =:= rpc:call(A, pg2, get_members, [G])), - ?line ?UNTIL([] =:= rpc:call(C, pg2, get_members, [G])), - - ?line ok = pg2:delete(G), - ?line stop_nodes([A,B,C]), - ?line test_server:timetrap_cancel(Dog), + ?UNTIL([] =:= rpc:call(B, pg2, get_members, [G])), + ?UNTIL([] =:= pg2:get_members(G)), + ?UNTIL([] =:= rpc:call(A, pg2, get_members, [G])), + ?UNTIL([] =:= rpc:call(C, pg2, get_members, [G])), + + ok = pg2:delete(G), + stop_nodes([A,B,C]), ok. part1(Config, Main, A, C, Name) -> case catch begin make_partition(Config, [Main, A], [node(), C]), - ?line {_Pid, yes} = start_proc(Name) + {_Pid, yes} = start_proc(Name) end of {_, yes} -> ok end. @@ -235,39 +218,10 @@ loop() -> exit(normal) end. -compat(suite) -> []; -compat(doc) -> - ["OTP-8259. Check that 'exchange' and 'del_member' work."]; -compat(Config) when is_list(Config) -> - case ?t:is_release_available("r13b") of - true -> - Timeout = 15, - ?line Dog = test_server:timetrap({seconds,Timeout}), - Pid = spawn(forever()), - G = a, - ?line ok = pg2:create(G), - ?line ok = pg2:join(G, Pid), - ?line ok = pg2:join(G, Pid), - ?line {ok, A} = start_node_rel(r13, r13b, slave), - ?line pong = net_adm:ping(A), - ?line wait_for_ready_net(Config), - ?line {ok, _} = rpc:call(A, pg2, start, []), - ?line ?UNTIL([Pid,Pid] =:= rpc:call(A, pg2, get_members, [a])), - ?line true = exit(Pid, kill), - ?line ?UNTIL([] =:= pg2:get_members(a)), - ?line ?UNTIL([] =:= rpc:call(A, pg2, get_members, [a])), - ?t:stop_node(A), - ?line test_server:timetrap_cancel(Dog); - false -> - {skipped, "No support for old node"} - end. - -basic(suite) -> []; -basic(doc) -> - ["OTP-8259. Some basic tests."]; +%% OTP-8259. Some basic tests. basic(Config) when is_list(Config) -> _ = [pg2:delete(G) || G <- pg2:which_groups()], - ?line _ = [do(Cs, T, Config) || {T,Cs} <- ts()], + _ = [do(Cs, T, Config) || {T,Cs} <- ts()], ok. ts() -> @@ -372,7 +326,7 @@ ts() -> ]. do(Cs, T, Config) -> - ?t:format("*** Test ~p ***~n", [T]), + io:format("*** Test ~p ***~n", [T]), {ok,T} = (catch {do(Cs, [], [], Config),T}). do([{nodeup,N} | Cs], Ps, Ns, Config) -> @@ -424,7 +378,7 @@ doit(N, C, Ps, Ns) -> Result when Result =:= R orelse R =:= ignore -> sane(Ns); Else -> - ?t:format("~p and ~p: expected ~p, but got ~p~n", + io:format("~p and ~p: expected ~p, but got ~p~n", [F, As, R, Else]), throw({error,{F, As, R, Else}}) end. @@ -445,8 +399,8 @@ killit(N, P, Ps, Ns) -> lists:keydelete(P, 1, Ps). pr(Node, C) -> - _ = [?t:format("~p: ", [Node]) || Node =/= node()], - ?t:format("do ~p~n", [C]). + _ = [io:format("~p: ", [Node]) || Node =/= node()], + io:format("do ~p~n", [C]). get_node(N, Ns) -> if @@ -474,7 +428,7 @@ replace_pids(A, Ps) -> sane(Ns) -> Nodes = [node()] ++ [NN || {_,NN} <- Ns], - _ = [?t:format("~p, pg2_table:~n ~p~n", % debug + _ = [io:format("~p, pg2_table:~n ~p~n", % debug [N, rpc:call(N, ets, tab2list, [pg2_table])]) || N <- Nodes], R = [case rpc:call(Node, ?MODULE, sane, []) of @@ -508,7 +462,7 @@ wsane(Ns) -> Pid when is_pid(Pid), node(Pid) =:= N -> true = lists:member(Pid, rpc:call(N, pg2, get_local_members, [G])); -%% FIXME. Om annan nod: member, local = []. + %% FIXME. Om annan nod: member, local = []. _ -> [] = rpc:call(N, pg2, get_local_members, [G]) end || N <- Ns] || G <- pg2:which_groups()]. @@ -557,21 +511,21 @@ start_node_rel(Name, Rel, How) -> Rel when is_atom(Rel) -> {[{release, atom_to_list(Rel)}], ""}; RelList -> - {RelList, ""} - end, - ?line Pa = filename:dirname(code:which(?MODULE)), - ?line Res = test_server:start_node(Name, How, - [{args, - Compat ++ - " -kernel net_setuptime 100 " - " -pa " ++ Pa}, - {erl, Release}]), + {RelList, ""} + end, + Pa = filename:dirname(code:which(?MODULE)), + Res = test_server:start_node(Name, How, + [{args, + Compat ++ + " -kernel net_setuptime 100 " + " -pa " ++ Pa}, + {erl, Release}]), Res. start_nodes(L, How, Config) -> start_nodes2(L, How, 0, Config), Nodes = collect_nodes(0, length(L)), - ?line ?UNTIL([] =:= Nodes -- nodes()), + ?UNTIL([] =:= Nodes -- nodes()), %% Pinging doesn't help, we have to wait too, for nodes() to become %% correct on the other node. lists:foreach(fun(E) -> @@ -587,7 +541,7 @@ verify_nodes(Nodes, Config) -> verify_nodes([], _N, _Config) -> []; verify_nodes([Node | Rest], N, Config) -> - ?line ?UNTIL( + ?UNTIL( case rpc:call(Node, erlang, nodes, []) of Nodes when is_list(Nodes) -> case N =:= lists:sort([Node | Nodes]) of @@ -619,7 +573,7 @@ start_nodes2([Name | Rest], How, N, Config) -> Self ! {N, R}, %% sleeping is necessary, or with peer nodes, they will %% go down again, despite {linked, false}. - test_server:sleep(100000) + ct:sleep(100000) end), start_nodes2(Rest, How, N+1, Config). @@ -639,7 +593,7 @@ start_node(Name0, How, Args, Config) -> Pa = filename:dirname(code:which(?MODULE)), test_server:start_node(Name, How, [{args, Args ++ " " ++ - "-kernel net_setuptime 100 " + "-kernel net_setuptime 100 " "-noshell " "-pa " ++ Pa}, {linked, false}]). @@ -647,7 +601,7 @@ stop_nodes(Nodes) -> lists:foreach(fun(Node) -> stop_node(Node) end, Nodes). stop_node(Node) -> - ?t:stop_node(Node). + test_server:stop_node(Node). get_known(Node) -> case catch gen_server:call({global_name_server,Node},get_known,infinity) of @@ -671,7 +625,7 @@ make_partition(Config, Part1, Part2) -> make_partition(Config, Part1, Part2, mk_part_node). make_partition(Config, Part1, Part2, Function) -> - Dir = ?config(priv_dir, Config), + Dir = proplists:get_value(priv_dir, Config), Ns = [begin Name = lists:concat([atom_to_list(N),"_",msec(),".part"]), File = filename:join([Dir, Name]), @@ -725,13 +679,13 @@ wait_for_ready_net(Config) -> wait_for_ready_net(Nodes0, Config) -> Nodes = lists:sort(Nodes0), - ?t:format("wait_for_ready_net ~p~n", [Nodes]), + io:format("wait_for_ready_net ~p~n", [Nodes]), ?UNTIL(begin lists:all(fun(N) -> Nodes =:= get_known(N) end, Nodes) and - lists:all(fun(N) -> - LNs = rpc:call(N, erlang, nodes, []), - Nodes =:= lists:sort([N | LNs]) - end, Nodes) + lists:all(fun(N) -> + LNs = rpc:call(N, erlang, nodes, []), + Nodes =:= lists:sort([N | LNs]) + end, Nodes) end). %% To make it less probable that some low-level problem causes @@ -787,11 +741,11 @@ file_contents(File, ContentsList, Config, LogFile) -> end). make_partition_file(Config) -> - Dir = ?config(priv_dir, Config), + Dir = proplists:get_value(priv_dir, Config), filename:join([Dir, atom_to_list(make_partition_done)]). msec() -> msec(now()). msec(T) -> - element(1,T)*1000000000 + element(2,T)*1000 + element(3,T) div 1000. + element(1,T)*1000000000 + element(2,T)*1000 + element(3,T) div 1000. diff --git a/lib/kernel/test/prim_file_SUITE.erl b/lib/kernel/test/prim_file_SUITE.erl index f55716cbec..a02b5f87d1 100644 --- a/lib/kernel/test/prim_file_SUITE.erl +++ b/lib/kernel/test/prim_file_SUITE.erl @@ -1,57 +1,43 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2000-2014. All Rights Reserved. +%% Copyright Ericsson AB 2000-2018. 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/. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at %% -%% 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. +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% -module(prim_file_SUITE). -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2, - read_write_file/1]). --export([cur_dir_0a/1, cur_dir_0b/1, - cur_dir_1a/1, cur_dir_1b/1, - make_del_dir_a/1, make_del_dir_b/1, - pos1/1, pos2/1]). --export([close/1, - delete_a/1, delete_b/1]). --export([ open1/1, modes/1]). --export([ - file_info_basic_file_a/1, file_info_basic_file_b/1, - file_info_basic_directory_a/1, file_info_basic_directory_b/1, - file_info_bad_a/1, file_info_bad_b/1, - file_info_times_a/1, file_info_times_b/1, - file_write_file_info_a/1, file_write_file_info_b/1, - file_read_file_info_opts/1, file_write_file_info_opts/1, - file_write_read_file_info_opts/1 - ]). --export([rename_a/1, rename_b/1, - access/1, truncate/1, datasync/1, sync/1, + init_per_group/2,end_per_group/2, init_per_testcase/2, end_per_testcase/2, + read_write_file/1, free_memory/0]). +-export([cur_dir_0/1, cur_dir_1/1, + make_del_dir/1, pos1/1, pos2/1]). +-export([close/1, delete/1]). +-export([open1/1, modes/1]). +-export([file_info_basic_file/1, file_info_basic_directory/1, file_info_bad/1, + file_info_times/1, file_write_file_info/1, + file_read_file_info_opts/1, file_write_file_info_opts/1, + file_write_read_file_info_opts/1]). +-export([rename/1, access/1, truncate/1, datasync/1, sync/1, read_write/1, pread_write/1, append/1, exclusive/1]). --export([ e_delete/1, e_rename/1, e_make_dir/1, e_del_dir/1]). - --export([ read_not_really_compressed/1, - read_compressed/1, write_compressed/1, - compress_errors/1]). +-export([e_delete/1, e_rename/1, e_make_dir/1, e_del_dir/1]). --export([ - make_link_a/1, make_link_b/1, - read_link_info_for_non_link/1, - symlinks_a/1, symlinks_b/1, - list_dir_limit/1, - list_dir_error/1, - list_dir/1]). +-export([make_link/1, read_link_info_for_non_link/1, + symlinks/1, + list_dir_limit/1, + list_dir_error/1, + list_dir/1]). -export([advise/1]). -export([large_write/1]). @@ -61,34 +47,21 @@ -export([allocate/1]). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -include_lib("kernel/include/file.hrl"). -define(PRIM_FILE, prim_file). -%% Calls ?PRIM_FILE:F with arguments A and an optional handle H -%% as first argument, unless the handle is [], i.e no handle. -%% This is a macro to give the compiler and thereby -%% the cross reference tool the possibility to interprete -%% the call, since M, F, A (or [H | A]) can all be known at -%% compile time. --define(PRIM_FILE_call(F, H, A), - case H of - [] -> apply(?PRIM_FILE, F, A); - _ -> apply(?PRIM_FILE, F, [H | A]) - end). - suite() -> []. all() -> [read_write_file, {group, dirs}, {group, files}, - delete_a, delete_b, rename_a, rename_b, {group, errors}, - {group, compression}, {group, links}, list_dir_limit, list_dir]. + delete, rename, {group, errors}, {group, links}, + list_dir_limit, list_dir]. groups() -> [{dirs, [], - [make_del_dir_a, make_del_dir_b, cur_dir_0a, cur_dir_0b, - cur_dir_1a, cur_dir_1b]}, + [make_del_dir, cur_dir_0, cur_dir_1]}, {files, [], [{group, open}, {group, pos}, {group, file_info}, truncate, sync, datasync, advise, large_write, allocate]}, @@ -97,22 +70,26 @@ groups() -> append, exclusive]}, {pos, [], [pos1, pos2]}, {file_info, [], - [file_info_basic_file_a, file_info_basic_file_b, - file_info_basic_directory_a, - file_info_basic_directory_b, file_info_bad_a, - file_info_bad_b, file_info_times_a, file_info_times_b, - file_write_file_info_a, file_write_file_info_b, - file_read_file_info_opts, file_write_file_info_opts, - file_write_read_file_info_opts - ]}, + [file_info_basic_file,file_info_basic_directory, file_info_bad, + file_info_times, file_write_file_info, file_read_file_info_opts, + file_write_file_info_opts, file_write_read_file_info_opts + ]}, {errors, [], [e_delete, e_rename, e_make_dir, e_del_dir]}, - {compression, [], - [read_compressed, read_not_really_compressed, - write_compressed, compress_errors]}, {links, [], - [make_link_a, make_link_b, read_link_info_for_non_link, - symlinks_a, symlinks_b, list_dir_error]}]. + [make_link, read_link_info_for_non_link, symlinks, list_dir_error]}]. + +init_per_testcase(large_write, Config) -> + {ok, Started} = application:ensure_all_started(os_mon), + [{started, Started}|Config]; +init_per_testcase(_TestCase, Config) -> + Config. + +end_per_testcase(large_write, Config) -> + [application:stop(App) || App <- lists:reverse(proplists:get_value(started, Config))], + ok; +end_per_testcase(_, _Config) -> + ok. init_per_group(_GroupName, Config) -> Config. @@ -124,7 +101,7 @@ end_per_group(_GroupName, Config) -> init_per_suite(Config) when is_list(Config) -> case os:type() of {win32, _} -> - Priv = ?config(priv_dir, Config), + Priv = proplists:get_value(priv_dir, Config), HasAccessTime = case file:read_file_info(Priv) of {ok, #file_info{atime={_, {0, 0, 0}}}} -> @@ -180,114 +157,96 @@ time_dist({_D1, _T1} = DT1, {_D2, _T2} = DT2) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -read_write_file(suite) -> []; -read_write_file(doc) -> []; read_write_file(Config) when is_list(Config) -> - ?line RootDir = ?config(priv_dir,Config), - ?line Name = filename:join(RootDir, - atom_to_list(?MODULE) - ++"_read_write_file"), + RootDir = proplists:get_value(priv_dir,Config), + Name = filename:join(RootDir, + atom_to_list(?MODULE) + ++"_read_write_file"), %% Try writing and reading back some term - ?line SomeTerm = {"This term",{will,be},[written,$t,$o],1,file,[]}, - ?line ok = ?PRIM_FILE:write_file(Name,term_to_binary(SomeTerm)), - ?line {ok,Bin1} = ?PRIM_FILE:read_file(Name), - ?line SomeTerm = binary_to_term(Bin1), - + SomeTerm = {"This term",{will,be},[written,$t,$o],1,file,[]}, + ok = ?PRIM_FILE:write_file(Name,term_to_binary(SomeTerm)), + {ok,Bin1} = ?PRIM_FILE:read_file(Name), + SomeTerm = binary_to_term(Bin1), + %% Try a "null" term - ?line NullTerm = [], - ?line ok = ?PRIM_FILE:write_file(Name,term_to_binary(NullTerm)), - ?line {ok,Bin2} = ?PRIM_FILE:read_file(Name), - ?line NullTerm = binary_to_term(Bin2), + NullTerm = [], + ok = ?PRIM_FILE:write_file(Name,term_to_binary(NullTerm)), + {ok,Bin2} = ?PRIM_FILE:read_file(Name), + NullTerm = binary_to_term(Bin2), %% Try some "complicated" types - ?line BigNum = 123456789012345678901234567890, - ?line ComplTerm = {self(),make_ref(),BigNum,3.14159}, - ?line ok = ?PRIM_FILE:write_file(Name,term_to_binary(ComplTerm)), - ?line {ok,Bin3} = ?PRIM_FILE:read_file(Name), - ?line ComplTerm = binary_to_term(Bin3), + BigNum = 123456789012345678901234567890, + ComplTerm = {self(),make_ref(),BigNum,3.14159}, + ok = ?PRIM_FILE:write_file(Name,term_to_binary(ComplTerm)), + {ok,Bin3} = ?PRIM_FILE:read_file(Name), + ComplTerm = binary_to_term(Bin3), %% Try reading a nonexistent file - ?line Name2 = filename:join(RootDir, - atom_to_list(?MODULE) - ++"_nonexistent_file"), - ?line {error, enoent} = ?PRIM_FILE:read_file(Name2), - ?line {error, enoent} = ?PRIM_FILE:read_file(""), - - % Try writing to a bad filename - ?line {error, enoent} = + Name2 = filename:join(RootDir, + atom_to_list(?MODULE) + ++"_nonexistent_file"), + {error, enoent} = ?PRIM_FILE:read_file(Name2), + {error, enoent} = ?PRIM_FILE:read_file(""), + + %% Try writing to a bad filename + {error, enoent} = ?PRIM_FILE:write_file("",term_to_binary(NullTerm)), - % Try writing something else than a binary - ?line {error, badarg} = ?PRIM_FILE:write_file(Name,{1,2,3}), - ?line {error, badarg} = ?PRIM_FILE:write_file(Name,self()), + %% Try writing something else than a binary + {error, badarg} = ?PRIM_FILE:write_file(Name,{1,2,3}), + {error, badarg} = ?PRIM_FILE:write_file(Name,self()), %% Some non-term binaries - ?line ok = ?PRIM_FILE:write_file(Name,[]), - ?line {ok,Bin4} = ?PRIM_FILE:read_file(Name), - ?line 0 = byte_size(Bin4), + ok = ?PRIM_FILE:write_file(Name,[]), + {ok,Bin4} = ?PRIM_FILE:read_file(Name), + 0 = byte_size(Bin4), - ?line ok = ?PRIM_FILE:write_file(Name,[Bin1,[],[[Bin2]]]), - ?line {ok,Bin5} = ?PRIM_FILE:read_file(Name), - ?line {Bin1,Bin2} = split_binary(Bin5,byte_size(Bin1)), + ok = ?PRIM_FILE:write_file(Name,[Bin1,[],[[Bin2]]]), + {ok,Bin5} = ?PRIM_FILE:read_file(Name), + {Bin1,Bin2} = split_binary(Bin5,byte_size(Bin1)), ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -make_del_dir_a(suite) -> []; -make_del_dir_a(doc) -> []; -make_del_dir_a(Config) when is_list(Config) -> - make_del_dir(Config, [], "_a"). - -make_del_dir_b(suite) -> []; -make_del_dir_b(doc) -> []; -make_del_dir_b(Config) when is_list(Config) -> - ?line {ok, Handle} = ?PRIM_FILE:start(), - Result = make_del_dir(Config, Handle, "_b"), - ?line ok = ?PRIM_FILE:stop(Handle), - %% Just to make sure the state of the server makes a difference - ?line {error, einval} = ?PRIM_FILE_call(get_cwd, Handle, []), - Result. - -make_del_dir(Config, Handle, Suffix) -> - ?line RootDir = ?config(priv_dir,Config), - ?line NewDir = filename:join(RootDir, - atom_to_list(?MODULE) - ++"_mk-dir"++Suffix), - ?line ok = ?PRIM_FILE_call(make_dir, Handle, [NewDir]), - ?line {error, eexist} = ?PRIM_FILE_call(make_dir, Handle, [NewDir]), - ?line ok = ?PRIM_FILE_call(del_dir, Handle, [NewDir]), - ?line {error, enoent} = ?PRIM_FILE_call(del_dir, Handle, [NewDir]), - - % Make sure we are not in a directory directly under test_server - % as that would result in eacces errors when trying to delete '..', - % because there are processes having that directory as current. - ?line ok = ?PRIM_FILE_call(make_dir, Handle, [NewDir]), - ?line {ok, CurrentDir} = ?PRIM_FILE_call(get_cwd, Handle, []), +make_del_dir(Config) when is_list(Config) -> + RootDir = proplists:get_value(priv_dir,Config), + NewDir = filename:join(RootDir, + atom_to_list(?MODULE) + ++"_mk-dir"), + ok = ?PRIM_FILE:make_dir(NewDir), + {error, eexist} = ?PRIM_FILE:make_dir(NewDir), + ok = ?PRIM_FILE:del_dir(NewDir), + {error, enoent} = ?PRIM_FILE:del_dir(NewDir), + + %% Make sure we are not in a directory directly under test_server + %% as that would result in eacces errors when trying to delete '..', + %% because there are processes having that directory as current. + ok = ?PRIM_FILE:make_dir(NewDir), + {ok, CurrentDir} = ?PRIM_FILE:get_cwd(), case {os:type(), length(NewDir) >= 260 } of {{win32,_}, true} -> io:format("Skip set_cwd for windows path longer than 260 (MAX_PATH)\n", []), io:format("\nNewDir = ~p\n", [NewDir]); _ -> - ok = ?PRIM_FILE_call(set_cwd, Handle, [NewDir]) + ok = ?PRIM_FILE:set_cwd(NewDir) end, try %% Check that we get an error when trying to create... %% a deep directory - ?line NewDir2 = filename:join(RootDir, - atom_to_list(?MODULE) - ++"_mk-dir-noexist/foo"), - ?line {error, enoent} = ?PRIM_FILE_call(make_dir, Handle, [NewDir2]), + NewDir2 = filename:join(RootDir, + atom_to_list(?MODULE) + ++"_mk-dir-noexist/foo"), + {error, enoent} = ?PRIM_FILE:make_dir(NewDir2), %% a nameless directory - ?line {error, enoent} = ?PRIM_FILE_call(make_dir, Handle, [""]), + {error, enoent} = ?PRIM_FILE:make_dir(""), %% a directory with illegal name - ?line {error, badarg} = ?PRIM_FILE_call(make_dir, Handle, ['mk-dir']), - + {error, badarg} = ?PRIM_FILE:make_dir('mk-dir'), + %% a directory with illegal name, even if it's a (bad) list - ?line {error, badarg} = ?PRIM_FILE_call(make_dir, Handle, [[1,2,3,{}]]), - + {error, badarg} = ?PRIM_FILE:make_dir([1,2,3,{}]), + %% Maybe this isn't an error, exactly, but worth mentioning anyway: %% ok = ?PRIM_FILE:make_dir([$f,$o,$o,0,$b,$a,$r])), %% The above line works, and created a directory "./foo" @@ -295,137 +254,105 @@ make_del_dir(Config, Handle, Suffix) -> %% a directory, but with a name that incorporates the "bar" part of %% the list, so that [$f,$o,$o,0,$f,$o,$o] wouldn't refer to the same %% dir. But this would slow it down. - + %% Try deleting some bad directories %% Deleting the parent directory to the current, sounds dangerous, huh? %% Don't worry ;-) the parent directory should never be empty, right? - ?line case ?PRIM_FILE_call(del_dir, Handle, [".."]) of - {error, eexist} -> ok; - {error, eacces} -> ok; %OpenBSD - {error, einval} -> ok %FreeBSD - end, - ?line {error, enoent} = ?PRIM_FILE_call(del_dir, Handle, [""]), - ?line {error, badarg} = ?PRIM_FILE_call(del_dir, Handle, [[3,2,1,{}]]) + case ?PRIM_FILE:del_dir("..") of + {error, eexist} -> ok; + {error, eacces} -> ok; %OpenBSD + {error, einval} -> ok %FreeBSD + end, + {error, enoent} = ?PRIM_FILE:del_dir(""), + {error, badarg} = ?PRIM_FILE:del_dir([3,2,1,{}]) after - ?line ok = ?PRIM_FILE_call(set_cwd, Handle, [CurrentDir]) + ok = ?PRIM_FILE:set_cwd(CurrentDir) end, ok. -cur_dir_0a(suite) -> []; -cur_dir_0a(doc) -> []; -cur_dir_0a(Config) when is_list(Config) -> - cur_dir_0(Config, []). - -cur_dir_0b(suite) -> []; -cur_dir_0b(doc) -> []; -cur_dir_0b(Config) when is_list(Config) -> - ?line {ok, Handle} = ?PRIM_FILE:start(), - Result = cur_dir_0(Config, Handle), - ?line ok = ?PRIM_FILE:stop(Handle), - Result. - -cur_dir_0(Config, Handle) -> +cur_dir_0(Config) when is_list(Config) -> %% Find out the current dir, and cd to it ;-) - ?line {ok,BaseDir} = ?PRIM_FILE_call(get_cwd, Handle, []), - ?line Dir1 = BaseDir ++ "", %% Check that it's a string - ?line ok = ?PRIM_FILE_call(set_cwd, Handle, [Dir1]), - ?line DirName = atom_to_list(?MODULE) ++ - case Handle of - [] -> - "_curdir"; - _ -> - "_curdir_h" - end, + {ok,BaseDir} = ?PRIM_FILE:get_cwd(), + Dir1 = BaseDir ++ "", %% Check that it's a string + ok = ?PRIM_FILE:set_cwd(Dir1), + DirName = atom_to_list(?MODULE) ++ "_curdir", %% Make a new dir, and cd to that - ?line RootDir = ?config(priv_dir,Config), - ?line NewDir = filename:join(RootDir, DirName), - ?line ok = ?PRIM_FILE_call(make_dir, Handle, [NewDir]), + RootDir = proplists:get_value(priv_dir,Config), + NewDir = filename:join(RootDir, DirName), + ok = ?PRIM_FILE:make_dir(NewDir), case {os:type(), length(NewDir) >= 260} of {{win32,_}, true} -> io:format("Skip set_cwd for windows path longer than 260 (MAX_PATH):\n"), io:format("\nNewDir = ~p\n", [NewDir]); _ -> io:format("cd to ~s",[NewDir]), - ok = ?PRIM_FILE_call(set_cwd, Handle, [NewDir]), + ok = ?PRIM_FILE:set_cwd(NewDir), %% Create a file in the new current directory, and check that it %% really is created there UncommonName = "uncommon.fil", {ok,Fd} = ?PRIM_FILE:open(UncommonName, [read, write]), ok = ?PRIM_FILE:close(Fd), - {ok,NewDirFiles} = ?PRIM_FILE_call(list_dir, Handle, ["."]), + {ok,NewDirFiles} = ?PRIM_FILE:list_dir("."), true = lists:member(UncommonName,NewDirFiles), %% Delete the directory and return to the old current directory %% and check that the created file isn't there (too!) expect({error, einval}, {error, eacces}, {error, eexist}, - ?PRIM_FILE_call(del_dir, Handle, [NewDir])), - ?PRIM_FILE_call(delete, Handle, [UncommonName]), - {ok,[]} = ?PRIM_FILE_call(list_dir, Handle, ["."]), - ok = ?PRIM_FILE_call(set_cwd, Handle, [Dir1]), + ?PRIM_FILE:del_dir(NewDir)), + ?PRIM_FILE:delete(UncommonName), + {ok,[]} = ?PRIM_FILE:list_dir("."), + ok = ?PRIM_FILE:set_cwd(Dir1), io:format("cd back to ~s",[Dir1]), - ok = ?PRIM_FILE_call(del_dir, Handle, [NewDir]), - {error, enoent} = ?PRIM_FILE_call(set_cwd, Handle, [NewDir]), - ok = ?PRIM_FILE_call(set_cwd, Handle, [Dir1]), + ok = ?PRIM_FILE:del_dir(NewDir), + {error, enoent} = ?PRIM_FILE:set_cwd(NewDir), + ok = ?PRIM_FILE:set_cwd(Dir1), io:format("cd back to ~s",[Dir1]), - {ok,OldDirFiles} = ?PRIM_FILE_call(list_dir, Handle, ["."]), + {ok,OldDirFiles} = ?PRIM_FILE:list_dir("."), false = lists:member(UncommonName,OldDirFiles) end, %% Try doing some bad things - ?line {error, badarg} = - ?PRIM_FILE_call(set_cwd, Handle, [{foo,bar}]), - ?line {error, enoent} = - ?PRIM_FILE_call(set_cwd, Handle, [""]), - ?line {error, enoent} = - ?PRIM_FILE_call(set_cwd, Handle, [".......a......"]), - ?line {ok,BaseDir} = - ?PRIM_FILE_call(get_cwd, Handle, []), %% Still there? + {error, badarg} = + ?PRIM_FILE:set_cwd({foo,bar}), + {error, enoent} = + ?PRIM_FILE:set_cwd(""), + {error, enoent} = + ?PRIM_FILE:set_cwd(".......a......"), + {ok,BaseDir} = + ?PRIM_FILE:get_cwd(), %% Still there? %% On Windows, there should only be slashes, no backslashes, %% in the return value of get_cwd(). %% (The test is harmless on Unix, because filenames usually %% don't contain backslashes.) - ?line {ok, BaseDir} = ?PRIM_FILE_call(get_cwd, Handle, []), - ?line false = lists:member($\\, BaseDir), + {ok, BaseDir} = ?PRIM_FILE:get_cwd(), + false = lists:member($\\, BaseDir), ok. %% Tests ?PRIM_FILE:get_cwd/1. -cur_dir_1a(suite) -> []; -cur_dir_1a(doc) -> []; -cur_dir_1a(Config) when is_list(Config) -> - cur_dir_1(Config, []). - -cur_dir_1b(suite) -> []; -cur_dir_1b(doc) -> []; -cur_dir_1b(Config) when is_list(Config) -> - ?line {ok, Handle} = ?PRIM_FILE:start(), - Result = cur_dir_1(Config, Handle), - ?line ok = ?PRIM_FILE:stop(Handle), - Result. - -cur_dir_1(Config, Handle) -> - ?line case os:type() of - {win32, _} -> - win_cur_dir_1(Config, Handle); - _ -> - ?line {error, enotsup} = - ?PRIM_FILE_call(get_cwd, Handle, ["d:"]) - end, +cur_dir_1(Config) when is_list(Config) -> + case os:type() of + {win32, _} -> + win_cur_dir_1(Config); + _ -> + {error, enotsup} = + ?PRIM_FILE:get_cwd("d:") + end, ok. - -win_cur_dir_1(_Config, Handle) -> - ?line {ok, BaseDir} = ?PRIM_FILE_call(get_cwd, Handle, []), + +win_cur_dir_1(_Config) -> + {ok, BaseDir} = ?PRIM_FILE:get_cwd(), %% Get the drive letter from the current directory, %% and try to get current directory for that drive. - ?line [Drive, $:|_] = BaseDir, - ?line {ok, BaseDir} = ?PRIM_FILE_call(get_cwd, Handle, [[Drive, $:]]), + [Drive, $:|_] = BaseDir, + {ok, BaseDir} = ?PRIM_FILE:get_cwd([Drive, $:]), io:format("BaseDir = ~s\n", [BaseDir]), %% Unfortunately, there is no way to move away from the @@ -438,526 +365,439 @@ win_cur_dir_1(_Config, Handle) -> -open1(suite) -> []; -open1(doc) -> []; open1(Config) when is_list(Config) -> - ?line RootDir = ?config(priv_dir,Config), - ?line NewDir = filename:join(RootDir, - atom_to_list(?MODULE) - ++"_files"), - ?line ok = ?PRIM_FILE:make_dir(NewDir), - ?line Name = filename:join(NewDir, "foo1.fil"), - ?line {ok,Fd1} = ?PRIM_FILE:open(Name, [read, write]), - ?line {ok,Fd2} = ?PRIM_FILE:open(Name, [read]), - ?line Str = "{a,tuple}.\n", - ?line Length = length(Str), - ?line ?PRIM_FILE:write(Fd1,Str), - ?line {ok,0} = ?PRIM_FILE:position(Fd1,bof), - ?line {ok, Str} = ?PRIM_FILE:read(Fd1,Length), - ?line case ?PRIM_FILE:read(Fd2,Length) of - {ok,Str} -> Str; - eof -> Str - end, - ?line ok = ?PRIM_FILE:close(Fd2), - ?line {ok,0} = ?PRIM_FILE:position(Fd1,bof), - ?line ok = ?PRIM_FILE:truncate(Fd1), - ?line eof = ?PRIM_FILE:read(Fd1,Length), - ?line ok = ?PRIM_FILE:close(Fd1), - ?line {ok,Fd3} = ?PRIM_FILE:open(Name, [read]), - ?line eof = ?PRIM_FILE:read(Fd3,Length), - ?line ok = ?PRIM_FILE:close(Fd3), + RootDir = proplists:get_value(priv_dir,Config), + NewDir = filename:join(RootDir, + atom_to_list(?MODULE) + ++"_files"), + ok = ?PRIM_FILE:make_dir(NewDir), + Name = filename:join(NewDir, "foo1.fil"), + {ok,Fd1} = ?PRIM_FILE:open(Name, [read, write]), + {ok,Fd2} = ?PRIM_FILE:open(Name, [read]), + Bin = list_to_binary("{a,tuple}.\n"), + Length = byte_size(Bin), + ?PRIM_FILE:write(Fd1,Bin), + {ok,0} = ?PRIM_FILE:position(Fd1,bof), + {ok, Bin} = ?PRIM_FILE:read(Fd1,Length), + {ok, Bin} = ?PRIM_FILE:read(Fd2,Length), + ok = ?PRIM_FILE:close(Fd2), + {ok,0} = ?PRIM_FILE:position(Fd1,bof), + ok = ?PRIM_FILE:truncate(Fd1), + eof = ?PRIM_FILE:read(Fd1,Length), + ok = ?PRIM_FILE:close(Fd1), + {ok,Fd3} = ?PRIM_FILE:open(Name, [read]), + eof = ?PRIM_FILE:read(Fd3,Length), + ok = ?PRIM_FILE:close(Fd3), ok. %% Tests all open modes. -modes(suite) -> []; -modes(doc) -> []; modes(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(10)), - ?line RootDir = ?config(priv_dir, Config), - ?line NewDir = filename:join(RootDir, - atom_to_list(?MODULE) - ++"_open_modes"), - ?line ok = ?PRIM_FILE:make_dir(NewDir), - ?line Name1 = filename:join(NewDir, "foo1.fil"), - ?line Marker = "hello, world", - ?line Length = length(Marker), + RootDir = proplists:get_value(priv_dir, Config), + NewDir = filename:join(RootDir, + atom_to_list(?MODULE) + ++"_open_modes"), + ok = ?PRIM_FILE:make_dir(NewDir), + Name1 = filename:join(NewDir, "foo1.fil"), + Marker = <<"hello, world">>, + Length = byte_size(Marker), %% write - ?line {ok, Fd1} = ?PRIM_FILE:open(Name1, [write]), - ?line ok = ?PRIM_FILE:write(Fd1, Marker), - ?line ok = ?PRIM_FILE:write(Fd1, ".\n"), - ?line ok = ?PRIM_FILE:close(Fd1), + {ok, Fd1} = ?PRIM_FILE:open(Name1, [write]), + ok = ?PRIM_FILE:write(Fd1, Marker), + ok = ?PRIM_FILE:write(Fd1, <<".\n">>), + ok = ?PRIM_FILE:close(Fd1), %% read - ?line {ok, Fd2} = ?PRIM_FILE:open(Name1, [read]), - ?line {ok, Marker} = ?PRIM_FILE:read(Fd2, Length), - ?line ok = ?PRIM_FILE:close(Fd2), + {ok, Fd2} = ?PRIM_FILE:open(Name1, [read]), + {ok, Marker} = ?PRIM_FILE:read(Fd2, Length), + ok = ?PRIM_FILE:close(Fd2), %% read and write - ?line {ok, Fd3} = ?PRIM_FILE:open(Name1, [read, write]), - ?line {ok, Marker} = ?PRIM_FILE:read(Fd3, Length), - ?line ok = ?PRIM_FILE:write(Fd3, Marker), - ?line ok = ?PRIM_FILE:close(Fd3), + {ok, Fd3} = ?PRIM_FILE:open(Name1, [read, write]), + {ok, Marker} = ?PRIM_FILE:read(Fd3, Length), + ok = ?PRIM_FILE:write(Fd3, Marker), + ok = ?PRIM_FILE:close(Fd3), %% read by default - ?line {ok, Fd4} = ?PRIM_FILE:open(Name1, []), - ?line {ok, Marker} = ?PRIM_FILE:read(Fd4, Length), - ?line ok = ?PRIM_FILE:close(Fd4), - - %% read and binary - ?line BinaryMarker = list_to_binary(Marker), - ?line {ok, Fd5} = ?PRIM_FILE:open(Name1, [read, binary]), - ?line {ok, BinaryMarker} = ?PRIM_FILE:read(Fd5, Length), - ?line ok = ?PRIM_FILE:close(Fd5), + {ok, Fd4} = ?PRIM_FILE:open(Name1, []), + {ok, Marker} = ?PRIM_FILE:read(Fd4, Length), + ok = ?PRIM_FILE:close(Fd4), - ?line test_server:timetrap_cancel(Dog), ok. -close(suite) -> []; -close(doc) -> []; close(Config) when is_list(Config) -> - ?line RootDir = ?config(priv_dir,Config), - ?line Name = filename:join(RootDir, - atom_to_list(?MODULE) - ++"_close.fil"), - ?line {ok,Fd1} = ?PRIM_FILE:open(Name, [read, write]), + RootDir = proplists:get_value(priv_dir,Config), + Name = filename:join(RootDir, + atom_to_list(?MODULE) + ++"_close.fil"), + {ok,Fd1} = ?PRIM_FILE:open(Name, [read, write]), %% Just closing it is no fun, we did that a million times already %% This is a common error, for code written before Erlang 4.3 %% bacause then ?PRIM_FILE:open just returned a Pid, and not everyone %% really checked what they got. - ?line {'EXIT',_Msg} = (catch ok = ?PRIM_FILE:close({ok,Fd1})), - ?line ok = ?PRIM_FILE:close(Fd1), + {'EXIT',_Msg} = (catch ok = ?PRIM_FILE:close({ok,Fd1})), + ok = ?PRIM_FILE:close(Fd1), %% Try closing one more time - ?line Val = ?PRIM_FILE:close(Fd1), - ?line io:format("Second close gave: ~p", [Val]), + Val = ?PRIM_FILE:close(Fd1), + io:format("Second close gave: ~p", [Val]), ok. -access(suite) -> []; -access(doc) -> []; access(Config) when is_list(Config) -> - ?line RootDir = ?config(priv_dir,Config), - ?line Name = filename:join(RootDir, - atom_to_list(?MODULE) - ++"_access.fil"), - ?line Str = "ABCDEFGH", - ?line {ok,Fd1} = ?PRIM_FILE:open(Name, [write]), - ?line ?PRIM_FILE:write(Fd1,Str), - ?line ok = ?PRIM_FILE:close(Fd1), + RootDir = proplists:get_value(priv_dir,Config), + Name = filename:join(RootDir, + atom_to_list(?MODULE) + ++"_access.fil"), + Bin = <<"ABCDEFGH">>, + {ok,Fd1} = ?PRIM_FILE:open(Name, [write]), + ?PRIM_FILE:write(Fd1,Bin), + ok = ?PRIM_FILE:close(Fd1), %% Check that we can't write when in read only mode - ?line {ok,Fd2} = ?PRIM_FILE:open(Name, [read]), - ?line case catch ?PRIM_FILE:write(Fd2,"XXXX") of - ok -> - test_server:fail({access,write}); - _ -> - ok - end, - ?line ok = ?PRIM_FILE:close(Fd2), - ?line {ok, Fd3} = ?PRIM_FILE:open(Name, [read]), - ?line {ok, Str} = ?PRIM_FILE:read(Fd3,length(Str)), - ?line ok = ?PRIM_FILE:close(Fd3), + {ok,Fd2} = ?PRIM_FILE:open(Name, [read]), + case catch ?PRIM_FILE:write(Fd2,"XXXX") of + ok -> + ct:fail({access,write}); + _ -> + ok + end, + ok = ?PRIM_FILE:close(Fd2), + {ok, Fd3} = ?PRIM_FILE:open(Name, [read]), + {ok, Bin} = ?PRIM_FILE:read(Fd3,byte_size(Bin)), + ok = ?PRIM_FILE:close(Fd3), ok. %% Tests ?PRIM_FILE:read/2 and ?PRIM_FILE:write/2. -read_write(suite) -> []; -read_write(doc) -> []; read_write(Config) when is_list(Config) -> - ?line RootDir = ?config(priv_dir, Config), - ?line NewDir = filename:join(RootDir, - atom_to_list(?MODULE) - ++"_read_write"), - ?line ok = ?PRIM_FILE:make_dir(NewDir), + RootDir = proplists:get_value(priv_dir, Config), + NewDir = filename:join(RootDir, + atom_to_list(?MODULE) + ++"_read_write"), + ok = ?PRIM_FILE:make_dir(NewDir), %% Raw file. - ?line Name = filename:join(NewDir, "raw.fil"), - ?line {ok, Fd} = ?PRIM_FILE:open(Name, [read, write]), - ?line read_write_test(Fd), + Name = filename:join(NewDir, "raw.fil"), + {ok, Fd} = ?PRIM_FILE:open(Name, [read, write]), + read_write_test(Fd), ok. read_write_test(File) -> - ?line Marker = "hello, world", - ?line ok = ?PRIM_FILE:write(File, Marker), - ?line {ok, 0} = ?PRIM_FILE:position(File, 0), - ?line {ok, Marker} = ?PRIM_FILE:read(File, 100), - ?line eof = ?PRIM_FILE:read(File, 100), - ?line ok = ?PRIM_FILE:close(File), + Marker = <<"hello, world">>, + ok = ?PRIM_FILE:write(File, Marker), + {ok, 0} = ?PRIM_FILE:position(File, 0), + {ok, Marker} = ?PRIM_FILE:read(File, 100), + eof = ?PRIM_FILE:read(File, 100), + ok = ?PRIM_FILE:close(File), ok. %% Tests ?PRIM_FILE:pread/2 and ?PRIM_FILE:pwrite/2. -pread_write(suite) -> []; -pread_write(doc) -> []; pread_write(Config) when is_list(Config) -> - ?line RootDir = ?config(priv_dir, Config), - ?line NewDir = filename:join(RootDir, - atom_to_list(?MODULE) - ++"_pread_write"), - ?line ok = ?PRIM_FILE:make_dir(NewDir), + RootDir = proplists:get_value(priv_dir, Config), + NewDir = filename:join(RootDir, + atom_to_list(?MODULE) + ++"_pread_write"), + ok = ?PRIM_FILE:make_dir(NewDir), %% Raw file. - ?line Name = filename:join(NewDir, "raw.fil"), - ?line {ok, Fd} = ?PRIM_FILE:open(Name, [read, write]), - ?line pread_write_test(Fd), + Name = filename:join(NewDir, "raw.fil"), + {ok, Fd} = ?PRIM_FILE:open(Name, [read, write]), + pread_write_test(Fd), ok. pread_write_test(File) -> - ?line Marker = "hello, world", - ?line Len = length(Marker), - ?line ok = ?PRIM_FILE:write(File, Marker), - ?line {ok, Marker} = ?PRIM_FILE:pread(File, 0, 100), - ?line eof = ?PRIM_FILE:pread(File, 100, 1), - ?line ok = ?PRIM_FILE:pwrite(File, Len, Marker), - ?line {ok, Marker} = ?PRIM_FILE:pread(File, Len, 100), - ?line eof = ?PRIM_FILE:pread(File, 100, 1), - ?line MM = Marker ++ Marker, - ?line {ok, MM} = ?PRIM_FILE:pread(File, 0, 100), - ?line ok = ?PRIM_FILE:close(File), + Marker = <<"hello, world">>, + Len = byte_size(Marker), + ok = ?PRIM_FILE:write(File, Marker), + {ok, Marker} = ?PRIM_FILE:pread(File, 0, 100), + eof = ?PRIM_FILE:pread(File, 100, 1), + ok = ?PRIM_FILE:pwrite(File, Len, Marker), + {ok, Marker} = ?PRIM_FILE:pread(File, Len, 100), + eof = ?PRIM_FILE:pread(File, 100, 1), + MM = <<Marker/binary,Marker/binary>>, + {ok, MM} = ?PRIM_FILE:pread(File, 0, 100), + ok = ?PRIM_FILE:close(File), ok. -append(doc) -> "Test appending to a file."; -append(suite) -> []; +%% Test appending to a file. append(Config) when is_list(Config) -> - ?line RootDir = ?config(priv_dir, Config), - ?line NewDir = filename:join(RootDir, - atom_to_list(?MODULE) - ++"_append"), - ?line ok = ?PRIM_FILE:make_dir(NewDir), + RootDir = proplists:get_value(priv_dir, Config), + NewDir = filename:join(RootDir, + atom_to_list(?MODULE) + ++"_append"), + ok = ?PRIM_FILE:make_dir(NewDir), - ?line First = "First line\n", - ?line Second = "Seond lines comes here\n", - ?line Third = "And here is the third line\n", + First = "First line\n", + Second = "Seond lines comes here\n", + Third = "And here is the third line\n", %% Write a small text file. - ?line Name1 = filename:join(NewDir, "a_file.txt"), - ?line {ok, Fd1} = ?PRIM_FILE:open(Name1, [write]), - ?line ok = ?PRIM_FILE:write(Fd1, First), - ?line ok = ?PRIM_FILE:write(Fd1, Second), - ?line ok = ?PRIM_FILE:close(Fd1), + Name1 = filename:join(NewDir, "a_file.txt"), + {ok, Fd1} = ?PRIM_FILE:open(Name1, [write]), + ok = ?PRIM_FILE:write(Fd1, First), + ok = ?PRIM_FILE:write(Fd1, Second), + ok = ?PRIM_FILE:close(Fd1), %% Open it a again and a append a line to it. - ?line {ok, Fd2} = ?PRIM_FILE:open(Name1, [append]), - ?line ok = ?PRIM_FILE:write(Fd2, Third), - ?line ok = ?PRIM_FILE:close(Fd2), + {ok, Fd2} = ?PRIM_FILE:open(Name1, [append]), + ok = ?PRIM_FILE:write(Fd2, Third), + ok = ?PRIM_FILE:close(Fd2), %% Read it back and verify. - ?line Expected = list_to_binary([First, Second, Third]), - ?line {ok, Expected} = ?PRIM_FILE:read_file(Name1), + Expected = list_to_binary([First, Second, Third]), + {ok, Expected} = ?PRIM_FILE:read_file(Name1), ok. -exclusive(suite) -> []; -exclusive(doc) -> "Test exclusive access to a file."; +%% Test exclusive access to a file. exclusive(Config) when is_list(Config) -> - ?line RootDir = ?config(priv_dir,Config), - ?line NewDir = filename:join(RootDir, - atom_to_list(?MODULE) - ++"_exclusive"), - ?line ok = ?PRIM_FILE:make_dir(NewDir), - ?line Name = filename:join(NewDir, "ex_file.txt"), - ?line {ok,Fd} = ?PRIM_FILE:open(Name, [write, exclusive]), - ?line {error, eexist} = ?PRIM_FILE:open(Name, [write, exclusive]), - ?line ok = ?PRIM_FILE:close(Fd), + RootDir = proplists:get_value(priv_dir,Config), + NewDir = filename:join(RootDir, + atom_to_list(?MODULE) + ++"_exclusive"), + ok = ?PRIM_FILE:make_dir(NewDir), + Name = filename:join(NewDir, "ex_file.txt"), + {ok,Fd} = ?PRIM_FILE:open(Name, [write, exclusive]), + {error, eexist} = ?PRIM_FILE:open(Name, [write, exclusive]), + ok = ?PRIM_FILE:close(Fd), ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -pos1(suite) -> []; -pos1(doc) -> []; pos1(Config) when is_list(Config) -> - ?line RootDir = ?config(priv_dir,Config), - ?line Name = filename:join(RootDir, - atom_to_list(?MODULE) - ++"_pos1.fil"), - ?line {ok, Fd1} = ?PRIM_FILE:open(Name, [write]), - ?line ?PRIM_FILE:write(Fd1,"ABCDEFGH"), - ?line ok = ?PRIM_FILE:close(Fd1), - ?line {ok, Fd2} = ?PRIM_FILE:open(Name, [read]), + RootDir = proplists:get_value(priv_dir,Config), + Name = filename:join(RootDir, + atom_to_list(?MODULE) + ++"_pos1.fil"), + {ok, Fd1} = ?PRIM_FILE:open(Name, [write]), + ?PRIM_FILE:write(Fd1,<<"ABCDEFGH">>), + ok = ?PRIM_FILE:close(Fd1), + {ok, Fd2} = ?PRIM_FILE:open(Name, [read]), %% Start pos is first char - ?line io:format("Relative positions"), - ?line {ok, "A"} = ?PRIM_FILE:read(Fd2,1), - ?line {ok, 2} = ?PRIM_FILE:position(Fd2,{cur,1}), - ?line {ok, "C"} = ?PRIM_FILE:read(Fd2,1), - ?line {ok, 0} = ?PRIM_FILE:position(Fd2,{cur,-3}), - ?line {ok, "A"} = ?PRIM_FILE:read(Fd2,1), + io:format("Relative positions"), + {ok, <<"A">>} = ?PRIM_FILE:read(Fd2,1), + {ok, 2} = ?PRIM_FILE:position(Fd2,{cur,1}), + {ok, <<"C">>} = ?PRIM_FILE:read(Fd2,1), + {ok, 0} = ?PRIM_FILE:position(Fd2,{cur,-3}), + {ok, <<"A">>} = ?PRIM_FILE:read(Fd2,1), %% Backwards from first char should be an error - ?line {ok,0} = ?PRIM_FILE:position(Fd2,{cur,-1}), - ?line {error, einval} = ?PRIM_FILE:position(Fd2,{cur,-1}), + {ok,0} = ?PRIM_FILE:position(Fd2,{cur,-1}), + {error, einval} = ?PRIM_FILE:position(Fd2,{cur,-1}), %% Reset position and move again - ?line {ok, 0} = ?PRIM_FILE:position(Fd2,0), - ?line {ok, 2} = ?PRIM_FILE:position(Fd2,{cur,2}), - ?line {ok, "C"} = ?PRIM_FILE:read(Fd2,1), + {ok, 0} = ?PRIM_FILE:position(Fd2,0), + {ok, 2} = ?PRIM_FILE:position(Fd2,{cur,2}), + {ok, <<"C">>} = ?PRIM_FILE:read(Fd2,1), %% Go a lot forwards - ?line {ok, 13} = ?PRIM_FILE:position(Fd2,{cur,10}), - ?line eof = ?PRIM_FILE:read(Fd2,1), + {ok, 13} = ?PRIM_FILE:position(Fd2,{cur,10}), + eof = ?PRIM_FILE:read(Fd2,1), %% Try some fixed positions - ?line io:format("Fixed positions"), - ?line {ok, 8} = ?PRIM_FILE:position(Fd2,8), - ?line eof = ?PRIM_FILE:read(Fd2,1), - ?line {ok, 8} = ?PRIM_FILE:position(Fd2,cur), - ?line eof = ?PRIM_FILE:read(Fd2,1), - ?line {ok, 7} = ?PRIM_FILE:position(Fd2,7), - ?line {ok, "H"} = ?PRIM_FILE:read(Fd2,1), - ?line {ok, 0} = ?PRIM_FILE:position(Fd2,0), - ?line {ok, "A"} = ?PRIM_FILE:read(Fd2,1), - ?line {ok, 3} = ?PRIM_FILE:position(Fd2,3), - ?line {ok, "D"} = ?PRIM_FILE:read(Fd2,1), - ?line {ok, 12} = ?PRIM_FILE:position(Fd2,12), - ?line eof = ?PRIM_FILE:read(Fd2,1), - ?line {ok, 3} = ?PRIM_FILE:position(Fd2,3), - ?line {ok, "D"} = ?PRIM_FILE:read(Fd2,1), + io:format("Fixed positions"), + {ok, 8} = ?PRIM_FILE:position(Fd2,8), + eof = ?PRIM_FILE:read(Fd2,1), + {ok, 8} = ?PRIM_FILE:position(Fd2,cur), + eof = ?PRIM_FILE:read(Fd2,1), + {ok, 7} = ?PRIM_FILE:position(Fd2,7), + {ok, <<"H">>} = ?PRIM_FILE:read(Fd2,1), + {ok, 0} = ?PRIM_FILE:position(Fd2,0), + {ok, <<"A">>} = ?PRIM_FILE:read(Fd2,1), + {ok, 3} = ?PRIM_FILE:position(Fd2,3), + {ok, <<"D">>} = ?PRIM_FILE:read(Fd2,1), + {ok, 12} = ?PRIM_FILE:position(Fd2,12), + eof = ?PRIM_FILE:read(Fd2,1), + {ok, 3} = ?PRIM_FILE:position(Fd2,3), + {ok, <<"D">>} = ?PRIM_FILE:read(Fd2,1), %% Try the {bof,X} notation - ?line {ok, 3} = ?PRIM_FILE:position(Fd2,{bof,3}), - ?line {ok, "D"} = ?PRIM_FILE:read(Fd2,1), + {ok, 3} = ?PRIM_FILE:position(Fd2,{bof,3}), + {ok, <<"D">>} = ?PRIM_FILE:read(Fd2,1), %% Try eof positions - ?line io:format("EOF positions"), - ?line {ok, 8} = ?PRIM_FILE:position(Fd2,{eof,0}), - ?line eof = ?PRIM_FILE:read(Fd2,1), - ?line {ok, 7} = ?PRIM_FILE:position(Fd2,{eof,-1}), - ?line {ok, "H"} = ?PRIM_FILE:read(Fd2,1), - ?line {ok, 0} = ?PRIM_FILE:position(Fd2,{eof,-8}), - ?line {ok, "A"} = ?PRIM_FILE:read(Fd2,1), - ?line {error, einval} = ?PRIM_FILE:position(Fd2,{eof,-9}), + io:format("EOF positions"), + {ok, 8} = ?PRIM_FILE:position(Fd2,{eof,0}), + eof = ?PRIM_FILE:read(Fd2,1), + {ok, 7} = ?PRIM_FILE:position(Fd2,{eof,-1}), + {ok, <<"H">>} = ?PRIM_FILE:read(Fd2,1), + {ok, 0} = ?PRIM_FILE:position(Fd2,{eof,-8}), + {ok, <<"A">>} = ?PRIM_FILE:read(Fd2,1), + {error, einval} = ?PRIM_FILE:position(Fd2,{eof,-9}), ok. -pos2(suite) -> []; -pos2(doc) -> []; pos2(Config) when is_list(Config) -> - ?line RootDir = ?config(priv_dir,Config), - ?line Name = filename:join(RootDir, - atom_to_list(?MODULE) - ++"_pos2.fil"), - ?line {ok,Fd1} = ?PRIM_FILE:open(Name, [write]), - ?line ?PRIM_FILE:write(Fd1,"ABCDEFGH"), - ?line ok = ?PRIM_FILE:close(Fd1), - ?line {ok, Fd2} = ?PRIM_FILE:open(Name, [read]), - ?line {error, einval} = ?PRIM_FILE:position(Fd2,-1), + RootDir = proplists:get_value(priv_dir,Config), + Name = filename:join(RootDir, + atom_to_list(?MODULE) + ++"_pos2.fil"), + {ok,Fd1} = ?PRIM_FILE:open(Name, [write]), + ?PRIM_FILE:write(Fd1,<<"ABCDEFGH">>), + ok = ?PRIM_FILE:close(Fd1), + {ok, Fd2} = ?PRIM_FILE:open(Name, [read]), + {error, einval} = ?PRIM_FILE:position(Fd2,-1), %% Make sure that we still can search after an error. - ?line {ok, 0} = ?PRIM_FILE:position(Fd2, 0), - ?line {ok, 3} = ?PRIM_FILE:position(Fd2, {bof,3}), - ?line {ok, "D"} = ?PRIM_FILE:read(Fd2,1), + {ok, 0} = ?PRIM_FILE:position(Fd2, 0), + {ok, 3} = ?PRIM_FILE:position(Fd2, {bof,3}), + {ok, <<"D">>} = ?PRIM_FILE:read(Fd2,1), - ?line io:format("DONE"), + io:format("DONE"), ok. - -file_info_basic_file_a(suite) -> []; -file_info_basic_file_a(doc) -> []; -file_info_basic_file_a(Config) when is_list(Config) -> - file_info_basic_file(Config, [], "_a"). - -file_info_basic_file_b(suite) -> []; -file_info_basic_file_b(doc) -> []; -file_info_basic_file_b(Config) when is_list(Config) -> - ?line {ok, Handle} = ?PRIM_FILE:start(), - Result = file_info_basic_file(Config, Handle, "_b"), - ?line ok = ?PRIM_FILE:stop(Handle), - Result. - -file_info_basic_file(Config, Handle, Suffix) -> - ?line RootDir = ?config(priv_dir, Config), +file_info_basic_file(Config) when is_list(Config) -> + RootDir = proplists:get_value(priv_dir, Config), %% Create a short file. - ?line Name = filename:join(RootDir, - atom_to_list(?MODULE) - ++"_basic_test"++Suffix++".fil"), - ?line {ok,Fd1} = ?PRIM_FILE:open(Name, [write]), - ?line ?PRIM_FILE:write(Fd1, "foo bar"), - ?line ok = ?PRIM_FILE:close(Fd1), + Name = filename:join(RootDir, + atom_to_list(?MODULE) + ++"_basic_test"".fil"), + {ok,Fd1} = ?PRIM_FILE:open(Name, [write]), + ?PRIM_FILE:write(Fd1, "foo bar"), + ok = ?PRIM_FILE:close(Fd1), %% Test that the file has the expected attributes. %% The times are tricky, so we will save them to a separate test case. - ?line {ok, FileInfo} = ?PRIM_FILE_call(read_file_info, Handle, [Name]), - ?line #file_info{size = Size, type = Type, access = Access, - atime = AccessTime, mtime = ModifyTime} = + {ok, FileInfo} = ?PRIM_FILE:read_file_info(Name), + #file_info{size = Size, type = Type, access = Access, + atime = AccessTime, mtime = ModifyTime} = FileInfo, - ?line io:format("Access ~p, Modify ~p", [AccessTime, ModifyTime]), - ?line Size = 7, - ?line Type = regular, - ?line Access = read_write, - ?line true = abs(time_dist(filter_atime(AccessTime, Config), - filter_atime(ModifyTime, - Config))) < 2, - ?line {AD, AT} = AccessTime, - ?line all_integers(tuple_to_list(AD) ++ tuple_to_list(AT)), - ?line {MD, MT} = ModifyTime, - ?line all_integers(tuple_to_list(MD) ++ tuple_to_list(MT)), + io:format("Access ~p, Modify ~p", [AccessTime, ModifyTime]), + Size = 7, + Type = regular, + Access = read_write, + true = abs(time_dist(filter_atime(AccessTime, Config), + filter_atime(ModifyTime, + Config))) < 2, + {AD, AT} = AccessTime, + all_integers(tuple_to_list(AD) ++ tuple_to_list(AT)), + {MD, MT} = ModifyTime, + all_integers(tuple_to_list(MD) ++ tuple_to_list(MT)), ok. -file_info_basic_directory_a(suite) -> []; -file_info_basic_directory_a(doc) -> []; -file_info_basic_directory_a(Config) when is_list(Config) -> - file_info_basic_directory(Config, []). - -file_info_basic_directory_b(suite) -> []; -file_info_basic_directory_b(doc) -> []; -file_info_basic_directory_b(Config) when is_list(Config) -> - ?line {ok, Handle} = ?PRIM_FILE:start(), - Result = file_info_basic_directory(Config, Handle), - ?line ok = ?PRIM_FILE:stop(Handle), - Result. - -file_info_basic_directory(Config, Handle) -> +file_info_basic_directory(Config) when is_list(Config) -> %% Note: filename:join/1 removes any trailing slash, %% which is essential for ?PRIM_FILE:read_file_info/1 to work on %% platforms such as Windows95. - ?line RootDir = filename:join([?config(priv_dir, Config)]), + RootDir = filename:join([proplists:get_value(priv_dir, Config)]), %% Test that the RootDir directory has the expected attributes. - ?line test_directory(RootDir, read_write, Handle), + test_directory(RootDir, read_write), %% Note that on Windows file systems, "/" or "c:/" are *NOT* directories. %% Therefore, test that ?PRIM_FILE:read_file_info/1 behaves %% as if they were directories. - ?line case os:type() of - {win32, _} -> - ?line test_directory("/", read_write, Handle), - ?line test_directory("c:/", read_write, Handle), - ?line test_directory("c:\\", read_write, Handle); - _ -> - ?line test_directory("/", read, Handle) - end, + case os:type() of + {win32, _} -> + test_directory("/", read_write), + test_directory("c:/", read_write), + test_directory("c:\\", read_write); + _ -> + test_directory("/", read) + end, ok. -test_directory(Name, ExpectedAccess, Handle) -> - ?line {ok, FileInfo} = ?PRIM_FILE_call(read_file_info, Handle, [Name]), - ?line #file_info{size = Size, type = Type, access = Access, - atime = AccessTime, mtime = ModifyTime} = +test_directory(Name, ExpectedAccess) -> + {ok, FileInfo} = ?PRIM_FILE:read_file_info(Name), + #file_info{size = Size, type = Type, access = Access, + atime = AccessTime, mtime = ModifyTime} = FileInfo, - ?line io:format("Testing directory ~s", [Name]), - ?line io:format("Directory size is ~p", [Size]), - ?line io:format("Access ~p", [Access]), - ?line io:format("Access time ~p; Modify time~p", - [AccessTime, ModifyTime]), - ?line Type = directory, - ?line Access = ExpectedAccess, - ?line {AD, AT} = AccessTime, - ?line all_integers(tuple_to_list(AD) ++ tuple_to_list(AT)), - ?line {MD, MT} = ModifyTime, - ?line all_integers(tuple_to_list(MD) ++ tuple_to_list(MT)), + io:format("Testing directory ~s", [Name]), + io:format("Directory size is ~p", [Size]), + io:format("Access ~p", [Access]), + io:format("Access time ~p; Modify time~p", + [AccessTime, ModifyTime]), + Type = directory, + Access = ExpectedAccess, + {AD, AT} = AccessTime, + all_integers(tuple_to_list(AD) ++ tuple_to_list(AT)), + {MD, MT} = ModifyTime, + all_integers(tuple_to_list(MD) ++ tuple_to_list(MT)), ok. all_integers([Int|Rest]) when is_integer(Int) -> - ?line all_integers(Rest); + all_integers(Rest); all_integers([]) -> ok. %% Try something nonexistent. -file_info_bad_a(suite) -> []; -file_info_bad_a(doc) -> []; -file_info_bad_a(Config) when is_list(Config) -> - file_info_bad(Config, []). - -file_info_bad_b(suite) -> []; -file_info_bad_b(doc) -> []; -file_info_bad_b(Config) when is_list(Config) -> - ?line {ok, Handle} = ?PRIM_FILE:start(), - Result = file_info_bad(Config, Handle), - ?line ok = ?PRIM_FILE:stop(Handle), - Result. - -file_info_bad(Config, Handle) -> - ?line RootDir = filename:join([?config(priv_dir, Config)]), - ?line {error, enoent} = - ?PRIM_FILE_call( - read_file_info, Handle, - [filename:join(RootDir, - atom_to_list(?MODULE)++"_nonexistent")]), +file_info_bad(Config) when is_list(Config) -> + RootDir = filename:join([proplists:get_value(priv_dir, Config)]), + NonExistent = filename:join(RootDir, atom_to_list(?MODULE)++"_nonexistent"), + {error, enoent} = ?PRIM_FILE:read_file_info(NonExistent), ok. %% Test that the file times behave as they should. -file_info_times_a(suite) -> []; -file_info_times_a(doc) -> []; -file_info_times_a(Config) when is_list(Config) -> - file_info_times(Config, [], "_a"). - -file_info_times_b(suite) -> []; -file_info_times_b(doc) -> []; -file_info_times_b(Config) when is_list(Config) -> - ?line {ok, Handle} = ?PRIM_FILE:start(), - Result = file_info_times(Config, Handle, "_b"), - ?line ok = ?PRIM_FILE:stop(Handle), - Result. - -file_info_times(Config, Handle, Suffix) -> - ?line Dog = test_server:timetrap(test_server:seconds(60)), +file_info_times(Config) when is_list(Config) -> %% We have to try this twice, since if the test runs across the change %% of a month the time diff calculations will fail. But it won't happen %% if you run it twice in succession. - ?line test_server:m_out_of_n( - 1,2, - fun() -> ?line file_info_int(Config, Handle, Suffix) end), - ?line test_server:timetrap_cancel(Dog), + test_server:m_out_of_n( + 1,2, + fun() -> file_info_int(Config) end), ok. -file_info_int(Config, Handle, Suffix) -> +file_info_int(Config) -> %% Note: filename:join/1 removes any trailing slash, %% which is essential for ?PRIM_FILE:read_file_info/1 to work on %% platforms such as Windows95. - ?line RootDir = filename:join([?config(priv_dir, Config)]), - ?line test_server:format("RootDir = ~p", [RootDir]), + RootDir = filename:join([proplists:get_value(priv_dir, Config)]), + io:format("RootDir = ~p", [RootDir]), - ?line Name = filename:join(RootDir, - atom_to_list(?MODULE) - ++"_file_info"++Suffix++".fil"), - ?line {ok,Fd1} = ?PRIM_FILE:open(Name, [write]), - ?line ?PRIM_FILE:write(Fd1,"foo"), + Name = filename:join(RootDir, + atom_to_list(?MODULE) + ++"_file_info.fil"), + {ok,Fd1} = ?PRIM_FILE:open(Name, [write]), + ?PRIM_FILE:write(Fd1,"foo"), %% check that the file got a modify date max a few seconds away from now - ?line {ok, #file_info{type = regular, - atime = AccTime1, mtime = ModTime1}} = - ?PRIM_FILE_call(read_file_info, Handle, [Name]), - ?line Now = erlang:localtime(), - ?line io:format("Now ~p",[Now]), - ?line io:format("Open file Acc ~p Mod ~p",[AccTime1,ModTime1]), - ?line true = abs(time_dist(filter_atime(Now, Config), - filter_atime(AccTime1, - Config))) < 8, - ?line true = abs(time_dist(Now, ModTime1)) < 8, - + {ok, #file_info{type = regular, + atime = AccTime1, mtime = ModTime1}} = + ?PRIM_FILE:read_file_info(Name), + Now = erlang:localtime(), + io:format("Now ~p",[Now]), + io:format("Open file Acc ~p Mod ~p",[AccTime1,ModTime1]), + true = abs(time_dist(filter_atime(Now, Config), + filter_atime(AccTime1, + Config))) < 8, + true = abs(time_dist(Now, ModTime1)) < 8, + %% Sleep until we can be sure the seconds value has changed. %% Note: FAT-based filesystem (like on Windows 95) have %% a resolution of 2 seconds. - ?line test_server:sleep(test_server:seconds(2.2)), + ct:sleep({seconds,2.2}), %% close the file, and watch the modify date change - ?line ok = ?PRIM_FILE:close(Fd1), - ?line {ok, #file_info{size = Size, type = regular, access = Access, - atime = AccTime2, mtime = ModTime2}} = - ?PRIM_FILE_call(read_file_info, Handle, [Name]), - ?line io:format("Closed file Acc ~p Mod ~p",[AccTime2,ModTime2]), - ?line true = time_dist(ModTime1, ModTime2) >= 0, + ok = ?PRIM_FILE:close(Fd1), + {ok, #file_info{size = Size, type = regular, access = Access, + atime = AccTime2, mtime = ModTime2}} = + ?PRIM_FILE:read_file_info(Name), + io:format("Closed file Acc ~p Mod ~p",[AccTime2,ModTime2]), + true = time_dist(ModTime1, ModTime2) >= 0, %% this file is supposed to be binary, so it'd better keep it's size - ?line Size = 3, - ?line Access = read_write, + Size = 3, + Access = read_write, %% Do some directory checking - ?line {ok, #file_info{size = DSize, type = directory, - access = DAccess, - atime = AccTime3, mtime = ModTime3}} = - ?PRIM_FILE_call(read_file_info, Handle, [RootDir]), + {ok, #file_info{size = DSize, type = directory, + access = DAccess, + atime = AccTime3, mtime = ModTime3}} = + ?PRIM_FILE:read_file_info(RootDir), %% this dir was modified only a few secs ago - ?line io:format("Dir Acc ~p; Mod ~p; Now ~p", - [AccTime3, ModTime3, Now]), - ?line true = abs(time_dist(Now, ModTime3)) < 5, - ?line DAccess = read_write, - ?line io:format("Dir size is ~p",[DSize]), + io:format("Dir Acc ~p; Mod ~p; Now ~p", + [AccTime3, ModTime3, Now]), + true = abs(time_dist(Now, ModTime3)) < 5, + DAccess = read_write, + io:format("Dir size is ~p",[DSize]), ok. %% Filter access times, to cope with a deficiency of FAT file systems @@ -968,9 +808,9 @@ filter_atime(Atime, Config) -> true -> case Atime of {Date, _} -> - {Date, {0, 0, 0}}; + {Date, {0, 0, 0}}; {Y, M, D, _, _, _} -> - {Y, M, D, 0, 0, 0} + {Y, M, D, 0, 0, 0} end; false -> Atime @@ -978,186 +818,151 @@ filter_atime(Atime, Config) -> %% Test the write_file_info/2 function. -file_write_file_info_a(suite) -> []; -file_write_file_info_a(doc) -> []; -file_write_file_info_a(Config) when is_list(Config) -> - file_write_file_info(Config, [], "_a"). - -file_write_file_info_b(suite) -> []; -file_write_file_info_b(doc) -> []; -file_write_file_info_b(Config) when is_list(Config) -> - ?line {ok, Handle} = ?PRIM_FILE:start(), - Result = file_write_file_info(Config, Handle, "_b"), - ?line ok = ?PRIM_FILE:stop(Handle), - Result. - -file_write_file_info(Config, Handle, Suffix) -> - ?line Dog = test_server:timetrap(test_server:seconds(10)), - ?line RootDir = get_good_directory(Config), - ?line test_server:format("RootDir = ~p", [RootDir]), +file_write_file_info(Config) when is_list(Config) -> + RootDir = get_good_directory(Config), + io:format("RootDir = ~p", [RootDir]), %% Set the file to read only AND update the file times at the same time. %% (This used to fail on Windows NT/95 for a local filesystem.) %% Note: Seconds must be even; see note in file_info_times/1. - ?line Name = filename:join(RootDir, - atom_to_list(?MODULE) - ++"_write_file_info_ro"++Suffix), - ?line ok = ?PRIM_FILE:write_file(Name, "hello"), - ?line Time = {{1997, 01, 02}, {12, 35, 42}}, - ?line Info = #file_info{mode=8#400, atime=Time, mtime=Time, ctime=Time}, - ?line ok = ?PRIM_FILE_call(write_file_info, Handle, [Name, Info]), + Name = filename:join(RootDir, + atom_to_list(?MODULE) + ++"_write_file_info_ro"), + ok = ?PRIM_FILE:write_file(Name, "hello"), + Time = {{1997, 01, 02}, {12, 35, 42}}, + Info = #file_info{mode=8#400, atime=Time, mtime=Time, ctime=Time}, + ok = ?PRIM_FILE:write_file_info(Name, Info), %% Read back the times. - ?line {ok, ActualInfo} = - ?PRIM_FILE_call(read_file_info, Handle, [Name]), - ?line #file_info{mode=_Mode, atime=ActAtime, mtime=Time, - ctime=ActCtime} = ActualInfo, - ?line FilteredAtime = filter_atime(Time, Config), - ?line FilteredAtime = filter_atime(ActAtime, Config), - ?line case os:type() of - {win32, _} -> - %% On Windows, "ctime" means creation time and it can - %% be set. - ActCtime = Time; - _ -> - ok - end, - ?line {error, eacces} = ?PRIM_FILE:write_file(Name, "hello again"), + {ok, ActualInfo} = + ?PRIM_FILE:read_file_info(Name), + #file_info{mode=_Mode, atime=ActAtime, mtime=Time, + ctime=ActCtime} = ActualInfo, + FilteredAtime = filter_atime(Time, Config), + FilteredAtime = filter_atime(ActAtime, Config), + case os:type() of + {win32, _} -> + %% On Windows, "ctime" means creation time and it can + %% be set. + ActCtime = Time; + _ -> + ok + end, + {error, eacces} = ?PRIM_FILE:write_file(Name, "hello again"), %% Make the file writable again. - - ?line ?PRIM_FILE_call(write_file_info, Handle, - [Name, #file_info{mode=8#600}]), - ?line ok = ?PRIM_FILE:write_file(Name, "hello again"), + ?PRIM_FILE:write_file_info(Name, #file_info{mode=8#600}), + ok = ?PRIM_FILE:write_file(Name, "hello again"), %% And unwritable. - ?line ?PRIM_FILE_call(write_file_info, Handle, - [Name, #file_info{mode=8#400}]), - ?line {error, eacces} = ?PRIM_FILE:write_file(Name, "hello again"), + ?PRIM_FILE:write_file_info(Name, #file_info{mode=8#400}), + {error, eacces} = ?PRIM_FILE:write_file(Name, "hello again"), %% Write the times again. %% Note: Seconds must be even; see note in file_info_times/1. - ?line NewTime = {{1997, 02, 15}, {13, 18, 20}}, - ?line NewInfo = #file_info{atime=NewTime, mtime=NewTime, ctime=NewTime}, - ?line ok = ?PRIM_FILE_call(write_file_info, Handle, [Name, NewInfo]), - ?line {ok, ActualInfo2} = - ?PRIM_FILE_call(read_file_info, Handle, [Name]), - ?line #file_info{atime=NewActAtime, mtime=NewTime, - ctime=NewActCtime} = ActualInfo2, - ?line NewFilteredAtime = filter_atime(NewTime, Config), - ?line NewFilteredAtime = filter_atime(NewActAtime, Config), - ?line case os:type() of - {win32, _} -> NewActCtime = NewTime; - _ -> ok - end, + NewTime = {{1997, 02, 15}, {13, 18, 20}}, + NewInfo = #file_info{atime=NewTime, mtime=NewTime, ctime=NewTime}, + ok = ?PRIM_FILE:write_file_info(Name, NewInfo), + {ok, ActualInfo2} = + ?PRIM_FILE:read_file_info(Name), + #file_info{atime=NewActAtime, mtime=NewTime, + ctime=NewActCtime} = ActualInfo2, + NewFilteredAtime = filter_atime(NewTime, Config), + NewFilteredAtime = filter_atime(NewActAtime, Config), + case os:type() of + {win32, _} -> NewActCtime = NewTime; + _ -> ok + end, %% The file should still be unwritable. - ?line {error, eacces} = ?PRIM_FILE:write_file(Name, "hello again"), + {error, eacces} = ?PRIM_FILE:write_file(Name, "hello again"), %% Make the file writeable again, so that we can remove the %% test suites ... :-) - ?line ?PRIM_FILE_call(write_file_info, Handle, - [Name, #file_info{mode=8#600}]), - ?line test_server:timetrap_cancel(Dog), + ?PRIM_FILE:write_file_info(Name, #file_info{mode=8#600}), ok. %% Test the write_file_info/3 function. -file_write_file_info_opts(suite) -> []; -file_write_file_info_opts(doc) -> []; file_write_file_info_opts(Config) when is_list(Config) -> - {ok, Handle} = ?PRIM_FILE:start(), - Dog = test_server:timetrap(test_server:seconds(10)), RootDir = get_good_directory(Config), - test_server:format("RootDir = ~p", [RootDir]), + io:format("RootDir = ~p", [RootDir]), Name = filename:join(RootDir, atom_to_list(?MODULE) ++"_write_file_info_opts"), ok = ?PRIM_FILE:write_file(Name, "hello_opts"), lists:foreach(fun - ({FI, Opts}) -> - ok = ?PRIM_FILE_call(write_file_info, Handle, [Name, FI, Opts]) - end, [ - {#file_info{ mode=8#600, atime = Time, mtime = Time, ctime = Time}, Opts} || - Opts <- [[{time, posix}]], - Time <- [ 0,1,-1,100,-100,1000,-1000,10000,-10000 ] - ]), - - % REM: determine date range dependent on time_t = Uint32 | Sint32 | Sint64 - % Determine time_t on os:type()? - lists:foreach(fun - ({FI, Opts}) -> - ok = ?PRIM_FILE_call(write_file_info, Handle, [Name, FI, Opts]) - end, [ - {#file_info{ mode=8#400, atime = Time, mtime = Time, ctime = Time}, Opts} || - Opts <- [[{time, universal}],[{time, local}]], - Time <- [ - {{1970,1,1},{0,0,0}}, - {{1970,1,1},{0,0,1}}, - {{1969,12,31},{23,59,59}}, - {{1908,2,3},{23,59,59}}, - {{2012,2,3},{23,59,59}}, - {{2037,2,3},{23,59,59}}, - erlang:localtime() - ]]), - ok = ?PRIM_FILE:stop(Handle), - test_server:timetrap_cancel(Dog), + ({FI, Opts}) -> + ok = ?PRIM_FILE:write_file_info(Name, FI, Opts) + end, [ + {#file_info{ mode=8#600, atime = Time, mtime = Time, ctime = Time}, Opts} || + Opts <- [[{time, posix}]], + Time <- [ 0,1,-1,100,-100,1000,-1000,10000,-10000 ] + ]), + + %% REM: determine date range dependent on time_t = Uint32 | Sint32 | Sint64 | Uint64 + %% Determine time_t on os:type()? + lists:foreach(fun ({FI, Opts}) -> + ok = ?PRIM_FILE:write_file_info(Name, FI, Opts) + end, [ {#file_info{ mode=8#400, atime = Time, mtime = Time, ctime = Time}, Opts} || + Opts <- [[{time, universal}],[{time, local}]], + Time <- [ + {{1970,1,1},{0,0,0}}, + {{1970,1,1},{0,0,1}}, + % {{1969,12,31},{23,59,59}}, + % {{1908,2,3},{23,59,59}}, + {{2012,2,3},{23,59,59}}, + {{2037,2,3},{23,59,59}}, + erlang:localtime() + ]]), ok. -file_read_file_info_opts(suite) -> []; -file_read_file_info_opts(doc) -> []; file_read_file_info_opts(Config) when is_list(Config) -> - {ok, Handle} = ?PRIM_FILE:start(), - Dog = test_server:timetrap(test_server:seconds(10)), RootDir = get_good_directory(Config), - test_server:format("RootDir = ~p", [RootDir]), + io:format("RootDir = ~p", [RootDir]), Name = filename:join(RootDir, atom_to_list(?MODULE) ++"_read_file_info_opts"), ok = ?PRIM_FILE:write_file(Name, "hello_opts"), lists:foreach(fun - (Opts) -> - {ok,_} = ?PRIM_FILE_call(read_file_info, Handle, [Name, Opts]) - end, [[{time, Type}] || Type <- [local, universal, posix]]), - ok = ?PRIM_FILE:stop(Handle), - test_server:timetrap_cancel(Dog), + (Opts) -> + {ok,_} = ?PRIM_FILE:read_file_info(Name, Opts) + end, [[{time, Type}] || Type <- [local, universal, posix]]), ok. %% Test the write and read back *_file_info/3 functions. -file_write_read_file_info_opts(suite) -> []; -file_write_read_file_info_opts(doc) -> []; file_write_read_file_info_opts(Config) when is_list(Config) -> - {ok, Handle} = ?PRIM_FILE:start(), - Dog = test_server:timetrap(test_server:seconds(10)), RootDir = get_good_directory(Config), - test_server:format("RootDir = ~p", [RootDir]), + io:format("RootDir = ~p", [RootDir]), Name = filename:join(RootDir, atom_to_list(?MODULE) ++"_read_write_file_info_opts"), ok = ?PRIM_FILE:write_file(Name, "hello_opts2"), - ok = file_write_read_file_info_opts(Handle, Name, {{1989, 04, 28}, {19,30,22}}, [{time, local}]), - ok = file_write_read_file_info_opts(Handle, Name, {{1989, 04, 28}, {19,30,22}}, [{time, universal}]), - ok = file_write_read_file_info_opts(Handle, Name, {{1930, 04, 28}, {19,30,22}}, [{time, local}]), - ok = file_write_read_file_info_opts(Handle, Name, {{1930, 04, 28}, {19,30,22}}, [{time, universal}]), - ok = file_write_read_file_info_opts(Handle, Name, 1, [{time, posix}]), - ok = file_write_read_file_info_opts(Handle, Name, -1, [{time, posix}]), - ok = file_write_read_file_info_opts(Handle, Name, 300000, [{time, posix}]), - ok = file_write_read_file_info_opts(Handle, Name, -300000, [{time, posix}]), - ok = file_write_read_file_info_opts(Handle, Name, 0, [{time, posix}]), - - ok = ?PRIM_FILE:stop(Handle), - test_server:timetrap_cancel(Dog), + ok = file_write_read_file_info_opts(Name, {{1989, 04, 28}, {19,30,22}}, [{time, local}]), + ok = file_write_read_file_info_opts(Name, {{1989, 04, 28}, {19,30,22}}, [{time, universal}]), + %% will not work on platforms with unsigned time_t + %ok = file_write_read_file_info_opts(Name, {{1930, 04, 28}, {19,30,22}}, [{time, local}]), + %ok = file_write_read_file_info_opts(Name, {{1930, 04, 28}, {19,30,22}}, [{time, universal}]), + ok = file_write_read_file_info_opts(Name, 1, [{time, posix}]), + %% will not work on platforms with unsigned time_t + %ok = file_write_read_file_info_opts(Name, -1, [{time, posix}]), + %ok = file_write_read_file_info_opts(Name, -300000, [{time, posix}]), + ok = file_write_read_file_info_opts(Name, 300000, [{time, posix}]), + ok = file_write_read_file_info_opts(Name, 0, [{time, posix}]), + ok. -file_write_read_file_info_opts(Handle, Name, Mtime, Opts) -> - {ok, FI} = ?PRIM_FILE_call(read_file_info, Handle, [Name, Opts]), +file_write_read_file_info_opts(Name, Mtime, Opts) -> + {ok, FI} = ?PRIM_FILE:read_file_info(Name, Opts), FI2 = FI#file_info{ mtime = Mtime }, - ok = ?PRIM_FILE_call(write_file_info, Handle, [Name, FI2, Opts]), - {ok, FI2} = ?PRIM_FILE_call(read_file_info, Handle, [Name, Opts]), + ok = ?PRIM_FILE:write_file_info(Name, FI2, Opts), + {ok, FI3} = ?PRIM_FILE:read_file_info(Name, Opts), + io:format("Expecting mtime = ~p, got ~p~n", [FI2#file_info.mtime, FI3#file_info.mtime]), + FI2 = FI3, ok. @@ -1165,136 +970,131 @@ file_write_read_file_info_opts(Handle, Name, Mtime, Opts) -> %% Returns a directory on a file system that has correct file times. get_good_directory(Config) -> - ?line ?config(priv_dir, Config). + proplists:get_value(priv_dir, Config). -truncate(suite) -> []; -truncate(doc) -> []; truncate(Config) when is_list(Config) -> - ?line RootDir = ?config(priv_dir,Config), - ?line Name = filename:join(RootDir, - atom_to_list(?MODULE) - ++"_truncate.fil"), + RootDir = proplists:get_value(priv_dir,Config), + Name = filename:join(RootDir, + atom_to_list(?MODULE) + ++"_truncate.fil"), %% Create a file with some data. - ?line MyData = "0123456789abcdefghijklmnopqrstuvxyz", - ?line ok = ?PRIM_FILE:write_file(Name, MyData), + MyData = "0123456789abcdefghijklmnopqrstuvxyz", + ok = ?PRIM_FILE:write_file(Name, MyData), %% Truncate the file to 10 characters. - ?line {ok, Fd} = ?PRIM_FILE:open(Name, [read, write]), - ?line {ok, 10} = ?PRIM_FILE:position(Fd, 10), - ?line ok = ?PRIM_FILE:truncate(Fd), - ?line ok = ?PRIM_FILE:close(Fd), + {ok, Fd} = ?PRIM_FILE:open(Name, [read, write]), + {ok, 10} = ?PRIM_FILE:position(Fd, 10), + ok = ?PRIM_FILE:truncate(Fd), + ok = ?PRIM_FILE:close(Fd), %% Read back the file and check that it has been truncated. - ?line Expected = list_to_binary("0123456789"), - ?line {ok, Expected} = ?PRIM_FILE:read_file(Name), + Expected = list_to_binary("0123456789"), + {ok, Expected} = ?PRIM_FILE:read_file(Name), %% Open the file read only and verify that it is not possible to %% truncate it, OTP-1960 - ?line {ok, Fd2} = ?PRIM_FILE:open(Name, [read]), - ?line {ok, 5} = ?PRIM_FILE:position(Fd2, 5), - ?line {error, _} = ?PRIM_FILE:truncate(Fd2), + {ok, Fd2} = ?PRIM_FILE:open(Name, [read]), + {ok, 5} = ?PRIM_FILE:position(Fd2, 5), + {error, _} = ?PRIM_FILE:truncate(Fd2), ok. -datasync(suite) -> []; -datasync(doc) -> "Tests that ?PRIM_FILE:datasync/1 at least doesn't crash."; +%% Tests that ?PRIM_FILE:datasync/1 at least doesn't crash. datasync(Config) when is_list(Config) -> - ?line PrivDir = ?config(priv_dir, Config), - ?line Sync = filename:join(PrivDir, - atom_to_list(?MODULE) - ++"_sync.fil"), + PrivDir = proplists:get_value(priv_dir, Config), + Sync = filename:join(PrivDir, + atom_to_list(?MODULE) + ++"_sync.fil"), %% Raw open. - ?line {ok, Fd} = ?PRIM_FILE:open(Sync, [write]), - ?line ok = ?PRIM_FILE:datasync(Fd), - ?line ok = ?PRIM_FILE:close(Fd), + {ok, Fd} = ?PRIM_FILE:open(Sync, [write]), + ok = ?PRIM_FILE:datasync(Fd), + ok = ?PRIM_FILE:close(Fd), ok. -sync(suite) -> []; -sync(doc) -> "Tests that ?PRIM_FILE:sync/1 at least doesn't crash."; +%% Tests that ?PRIM_FILE:sync/1 at least doesn't crash. sync(Config) when is_list(Config) -> - ?line PrivDir = ?config(priv_dir, Config), - ?line Sync = filename:join(PrivDir, - atom_to_list(?MODULE) - ++"_sync.fil"), + PrivDir = proplists:get_value(priv_dir, Config), + Sync = filename:join(PrivDir, + atom_to_list(?MODULE) + ++"_sync.fil"), %% Raw open. - ?line {ok, Fd} = ?PRIM_FILE:open(Sync, [write]), - ?line ok = ?PRIM_FILE:sync(Fd), - ?line ok = ?PRIM_FILE:close(Fd), + {ok, Fd} = ?PRIM_FILE:open(Sync, [write]), + ok = ?PRIM_FILE:sync(Fd), + ok = ?PRIM_FILE:close(Fd), ok. -advise(suite) -> []; -advise(doc) -> "Tests that ?PRIM_FILE:advise/4 at least doesn't crash."; +%% Tests that ?PRIM_FILE:advise/4 at least doesn't crash. advise(Config) when is_list(Config) -> - ?line PrivDir = ?config(priv_dir, Config), - ?line Advise = filename:join(PrivDir, - atom_to_list(?MODULE) - ++"_advise.fil"), - - Line1 = "Hello\n", - Line2 = "World!\n", - - ?line {ok, Fd} = ?PRIM_FILE:open(Advise, [write]), - ?line ok = ?PRIM_FILE:advise(Fd, 0, 0, normal), - ?line ok = ?PRIM_FILE:write(Fd, Line1), - ?line ok = ?PRIM_FILE:write(Fd, Line2), - ?line ok = ?PRIM_FILE:close(Fd), - - ?line {ok, Fd2} = ?PRIM_FILE:open(Advise, [write]), - ?line ok = ?PRIM_FILE:advise(Fd2, 0, 0, random), - ?line ok = ?PRIM_FILE:write(Fd2, Line1), - ?line ok = ?PRIM_FILE:write(Fd2, Line2), - ?line ok = ?PRIM_FILE:close(Fd2), - - ?line {ok, Fd3} = ?PRIM_FILE:open(Advise, [write]), - ?line ok = ?PRIM_FILE:advise(Fd3, 0, 0, sequential), - ?line ok = ?PRIM_FILE:write(Fd3, Line1), - ?line ok = ?PRIM_FILE:write(Fd3, Line2), - ?line ok = ?PRIM_FILE:close(Fd3), - - ?line {ok, Fd4} = ?PRIM_FILE:open(Advise, [write]), - ?line ok = ?PRIM_FILE:advise(Fd4, 0, 0, will_need), - ?line ok = ?PRIM_FILE:write(Fd4, Line1), - ?line ok = ?PRIM_FILE:write(Fd4, Line2), - ?line ok = ?PRIM_FILE:close(Fd4), - - ?line {ok, Fd5} = ?PRIM_FILE:open(Advise, [write]), - ?line ok = ?PRIM_FILE:advise(Fd5, 0, 0, dont_need), - ?line ok = ?PRIM_FILE:write(Fd5, Line1), - ?line ok = ?PRIM_FILE:write(Fd5, Line2), - ?line ok = ?PRIM_FILE:close(Fd5), - - ?line {ok, Fd6} = ?PRIM_FILE:open(Advise, [write]), - ?line ok = ?PRIM_FILE:advise(Fd6, 0, 0, no_reuse), - ?line ok = ?PRIM_FILE:write(Fd6, Line1), - ?line ok = ?PRIM_FILE:write(Fd6, Line2), - ?line ok = ?PRIM_FILE:close(Fd6), - - ?line {ok, Fd7} = ?PRIM_FILE:open(Advise, [write]), - ?line {error, einval} = ?PRIM_FILE:advise(Fd7, 0, 0, bad_advise), - ?line ok = ?PRIM_FILE:close(Fd7), + PrivDir = proplists:get_value(priv_dir, Config), + Advise = filename:join(PrivDir, + atom_to_list(?MODULE) + ++"_advise.fil"), + + Line1 = <<"Hello\n">>, + Line2 = <<"World!\n">>, + + {ok, Fd} = ?PRIM_FILE:open(Advise, [write]), + ok = ?PRIM_FILE:advise(Fd, 0, 0, normal), + ok = ?PRIM_FILE:write(Fd, Line1), + ok = ?PRIM_FILE:write(Fd, Line2), + ok = ?PRIM_FILE:close(Fd), + + {ok, Fd2} = ?PRIM_FILE:open(Advise, [write]), + ok = ?PRIM_FILE:advise(Fd2, 0, 0, random), + ok = ?PRIM_FILE:write(Fd2, Line1), + ok = ?PRIM_FILE:write(Fd2, Line2), + ok = ?PRIM_FILE:close(Fd2), + + {ok, Fd3} = ?PRIM_FILE:open(Advise, [write]), + ok = ?PRIM_FILE:advise(Fd3, 0, 0, sequential), + ok = ?PRIM_FILE:write(Fd3, Line1), + ok = ?PRIM_FILE:write(Fd3, Line2), + ok = ?PRIM_FILE:close(Fd3), + + {ok, Fd4} = ?PRIM_FILE:open(Advise, [write]), + ok = ?PRIM_FILE:advise(Fd4, 0, 0, will_need), + ok = ?PRIM_FILE:write(Fd4, Line1), + ok = ?PRIM_FILE:write(Fd4, Line2), + ok = ?PRIM_FILE:close(Fd4), + + {ok, Fd5} = ?PRIM_FILE:open(Advise, [write]), + ok = ?PRIM_FILE:advise(Fd5, 0, 0, dont_need), + ok = ?PRIM_FILE:write(Fd5, Line1), + ok = ?PRIM_FILE:write(Fd5, Line2), + ok = ?PRIM_FILE:close(Fd5), + + {ok, Fd6} = ?PRIM_FILE:open(Advise, [write]), + ok = ?PRIM_FILE:advise(Fd6, 0, 0, no_reuse), + ok = ?PRIM_FILE:write(Fd6, Line1), + ok = ?PRIM_FILE:write(Fd6, Line2), + ok = ?PRIM_FILE:close(Fd6), + + {ok, Fd7} = ?PRIM_FILE:open(Advise, [write]), + {error, einval} = ?PRIM_FILE:advise(Fd7, 0, 0, bad_advise), + ok = ?PRIM_FILE:close(Fd7), %% test write without advise, then a read after an advise - ?line {ok, Fd8} = ?PRIM_FILE:open(Advise, [write]), - ?line ok = ?PRIM_FILE:write(Fd8, Line1), - ?line ok = ?PRIM_FILE:write(Fd8, Line2), - ?line ok = ?PRIM_FILE:close(Fd8), - ?line {ok, Fd9} = ?PRIM_FILE:open(Advise, [read]), + {ok, Fd8} = ?PRIM_FILE:open(Advise, [write]), + ok = ?PRIM_FILE:write(Fd8, Line1), + ok = ?PRIM_FILE:write(Fd8, Line2), + ok = ?PRIM_FILE:close(Fd8), + {ok, Fd9} = ?PRIM_FILE:open(Advise, [read]), Offset = 0, %% same as a 0 length in some implementations - Length = length(Line1) + length(Line2), - ?line ok = ?PRIM_FILE:advise(Fd9, Offset, Length, sequential), - ?line {ok, Line1} = ?PRIM_FILE:read_line(Fd9), - ?line {ok, Line2} = ?PRIM_FILE:read_line(Fd9), - ?line eof = ?PRIM_FILE:read_line(Fd9), - ?line ok = ?PRIM_FILE:close(Fd9), + Length = byte_size(Line1) + byte_size(Line2), + ok = ?PRIM_FILE:advise(Fd9, Offset, Length, sequential), + {ok, Line1} = ?PRIM_FILE:read_line(Fd9), + {ok, Line2} = ?PRIM_FILE:read_line(Fd9), + eof = ?PRIM_FILE:read_line(Fd9), + ok = ?PRIM_FILE:close(Fd9), ok. @@ -1306,7 +1106,6 @@ large_write(Config) when is_list(Config) -> "_large_write"). do_large_write(Name) -> - Dog = test_server:timetrap(test_server:minutes(60)), ChunkSize = (256 bsl 20) + 1, % 256 M + 1 Chunks = 16, % times 16 -> 4 G + 16 Base = 100, @@ -1314,192 +1113,159 @@ do_large_write(Name) -> Chunk = <<0:ChunkSize/unit:8>>, Data = zip_data(lists:duplicate(Chunks, Chunk), Interleave), Size = Chunks * ChunkSize + Chunks, % 4 G + 32 - Wordsize = erlang:system_info(wordsize), - case prim_file:write_file(Name, Data) of - ok when Wordsize =:= 8 -> - {ok,#file_info{size=Size}} = file:read_file_info(Name), - {ok,Fd} = prim_file:open(Name, [read]), - check_large_write(Dog, Fd, ChunkSize, 0, Interleave); - {error,einval} when Wordsize =:= 4 -> - ok - end. + ok = ?PRIM_FILE:write_file(Name, Data), + {ok,#file_info{size=Size}} = file:read_file_info(Name), + {ok,Fd} = ?PRIM_FILE:open(Name, [read]), + check_large_write(Fd, ChunkSize, 0, Interleave). -check_large_write(Dog, Fd, ChunkSize, Pos, [X|Interleave]) -> +check_large_write(Fd, ChunkSize, Pos, [X|Interleave]) -> Pos1 = Pos + ChunkSize, - {ok,Pos1} = prim_file:position(Fd, {cur,ChunkSize}), - {ok,[X]} = prim_file:read(Fd, 1), - check_large_write(Dog, Fd, ChunkSize, Pos1+1, Interleave); -check_large_write(Dog, Fd, _, _, []) -> - eof = prim_file:read(Fd, 1), - test_server:timetrap_cancel(Dog), + {ok,Pos1} = ?PRIM_FILE:position(Fd, {cur,ChunkSize}), + {ok,<<X>>} = ?PRIM_FILE:read(Fd, 1), + check_large_write(Fd, ChunkSize, Pos1+1, Interleave); +check_large_write(Fd, _, _, []) -> + eof = ?PRIM_FILE:read(Fd, 1), ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -allocate(suite) -> []; -allocate(doc) -> "Tests that ?PRIM_FILE:allocate/3 at least doesn't crash."; +%% Tests that ?PRIM_FILE:allocate/3 at least doesn't crash. allocate(Config) when is_list(Config) -> - ?line PrivDir = ?config(priv_dir, Config), - ?line Allocate = filename:join(PrivDir, - atom_to_list(?MODULE) - ++"_allocate.fil"), + PrivDir = proplists:get_value(priv_dir, Config), + Allocate = filename:join(PrivDir, + atom_to_list(?MODULE) + ++"_allocate.fil"), Line1 = "Hello\n", Line2 = "World!\n", - ?line {ok, Fd} = ?PRIM_FILE:open(Allocate, [write, binary]), + {ok, Fd} = ?PRIM_FILE:open(Allocate, [write, binary]), allocate_and_assert(Fd, 1, iolist_size([Line1, Line2])), - ?line ok = ?PRIM_FILE:write(Fd, Line1), - ?line ok = ?PRIM_FILE:write(Fd, Line2), - ?line ok = ?PRIM_FILE:close(Fd), + ok = ?PRIM_FILE:write(Fd, Line1), + ok = ?PRIM_FILE:write(Fd, Line2), + ok = ?PRIM_FILE:close(Fd), - ?line {ok, Fd2} = ?PRIM_FILE:open(Allocate, [write, binary]), + {ok, Fd2} = ?PRIM_FILE:open(Allocate, [write, binary]), allocate_and_assert(Fd2, 1, iolist_size(Line1)), - ?line ok = ?PRIM_FILE:write(Fd2, Line1), - ?line ok = ?PRIM_FILE:write(Fd2, Line2), - ?line ok = ?PRIM_FILE:close(Fd2), + ok = ?PRIM_FILE:write(Fd2, Line1), + ok = ?PRIM_FILE:write(Fd2, Line2), + ok = ?PRIM_FILE:close(Fd2), - ?line {ok, Fd3} = ?PRIM_FILE:open(Allocate, [write, binary]), + {ok, Fd3} = ?PRIM_FILE:open(Allocate, [write, binary]), allocate_and_assert(Fd3, 1, iolist_size(Line1) + 1), - ?line ok = ?PRIM_FILE:write(Fd3, Line1), - ?line ok = ?PRIM_FILE:write(Fd3, Line2), - ?line ok = ?PRIM_FILE:close(Fd3), + ok = ?PRIM_FILE:write(Fd3, Line1), + ok = ?PRIM_FILE:write(Fd3, Line2), + ok = ?PRIM_FILE:close(Fd3), - ?line {ok, Fd4} = ?PRIM_FILE:open(Allocate, [write, binary]), + {ok, Fd4} = ?PRIM_FILE:open(Allocate, [write, binary]), allocate_and_assert(Fd4, 1, 4 * iolist_size([Line1, Line2])), - ?line ok = ?PRIM_FILE:write(Fd4, Line1), - ?line ok = ?PRIM_FILE:write(Fd4, Line2), - ?line ok = ?PRIM_FILE:close(Fd4), + ok = ?PRIM_FILE:write(Fd4, Line1), + ok = ?PRIM_FILE:write(Fd4, Line2), + ok = ?PRIM_FILE:close(Fd4), ok. allocate_and_assert(Fd, Offset, Length) -> - % Just verify that calls to ?PRIM_FILE:allocate/3 don't crash or have - % any other negative side effect. We can't really asssert against a - % specific return value, because support for file space pre-allocation - % depends on the OS, OS version and underlying filesystem. - % - % The Linux kernel added support for fallocate() in version 2.6.23, - % which currently works only for the ext4, ocfs2, xfs and btrfs file - % systems. posix_fallocate() is available in glibc as of version - % 2.1.94, but it was buggy until glibc version 2.7. - % - % Mac OS X, as of version 10.3, supports the fcntl operation F_PREALLOCATE. - % - % Solaris supports posix_fallocate() but only for the UFS file system - % apparently (not supported for ZFS). - % - % FreeBSD 9.0 is the first FreeBSD release supporting posix_fallocate(). - % - % For Windows there's apparently no way to pre-allocate file space, at - % least with similar API/semantics as posix_fallocate(), fallocate() or - % fcntl F_PREALLOCATE. + %% Just verify that calls to ?PRIM_FILE:allocate/3 don't crash or have + %% any other negative side effect. We can't really asssert against a + %% specific return value, because support for file space pre-allocation + %% depends on the OS, OS version and underlying filesystem. + %% + %% The Linux kernel added support for fallocate() in version 2.6.23, + %% which currently works only for the ext4, ocfs2, xfs and btrfs file + %% systems. posix_fallocate() is available in glibc as of version + %% 2.1.94, but it was buggy until glibc version 2.7. + %% + %% Mac OS X, as of version 10.3, supports the fcntl operation F_PREALLOCATE. + %% + %% Solaris supports posix_fallocate() but only for the UFS file system + %% apparently (not supported for ZFS). + %% + %% FreeBSD 9.0 is the first FreeBSD release supporting posix_fallocate(). + %% + %% For Windows there's apparently no way to pre-allocate file space, at + %% least with similar API/semantics as posix_fallocate(), fallocate() or + %% fcntl F_PREALLOCATE. Result = ?PRIM_FILE:allocate(Fd, Offset, Length), case os:type() of {win32, _} -> - ?line {error, enotsup} = Result; + {error, enotsup} = Result; _ -> - ?line _ = Result + _ = Result end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -delete_a(suite) -> []; -delete_a(doc) -> []; -delete_a(Config) when is_list(Config) -> - delete(Config, [], "_a"). - -delete_b(suite) -> []; -delete_b(doc) -> []; -delete_b(Config) when is_list(Config) -> - ?line {ok, Handle} = ?PRIM_FILE:start(), - Result = delete(Config, Handle, "_b"), - ?line ok = ?PRIM_FILE:stop(Handle), - Result. - -delete(Config, Handle, Suffix) -> - ?line RootDir = ?config(priv_dir,Config), - ?line Name = filename:join(RootDir, - atom_to_list(?MODULE) - ++"_delete"++Suffix++".fil"), - ?line {ok, Fd1} = ?PRIM_FILE:open(Name, [write]), - ?line ?PRIM_FILE:write(Fd1,"ok.\n"), - ?line ok = ?PRIM_FILE:close(Fd1), +delete(Config) when is_list(Config) -> + RootDir = proplists:get_value(priv_dir,Config), + Name = filename:join(RootDir, + atom_to_list(?MODULE) + ++"_delete.fil"), + {ok, Fd1} = ?PRIM_FILE:open(Name, [write]), + ?PRIM_FILE:write(Fd1,"ok.\n"), + ok = ?PRIM_FILE:close(Fd1), %% Check that the file is readable - ?line {ok, Fd2} = ?PRIM_FILE:open(Name, [read]), - ?line ok = ?PRIM_FILE:close(Fd2), - ?line ok = ?PRIM_FILE_call(delete, Handle, [Name]), + {ok, Fd2} = ?PRIM_FILE:open(Name, [read]), + ok = ?PRIM_FILE:close(Fd2), + ok = ?PRIM_FILE:delete(Name), %% Check that the file is not readable anymore - ?line {error, _} = ?PRIM_FILE:open(Name, [read]), + {error, _} = ?PRIM_FILE:open(Name, [read]), %% Try deleting a nonexistent file - ?line {error, enoent} = ?PRIM_FILE_call(delete, Handle, [Name]), + {error, enoent} = ?PRIM_FILE:delete(Name), ok. -rename_a(suite) ->[]; -rename_a(doc) ->[]; -rename_a(Config) when is_list(Config) -> - rename(Config, [], "_a"). - -rename_b(suite) ->[]; -rename_b(doc) ->[]; -rename_b(Config) when is_list(Config) -> - ?line {ok, Handle} = ?PRIM_FILE:start(), - Result = rename(Config, Handle, "_b"), - ?line ok = ?PRIM_FILE:stop(Handle), - Result. - -rename(Config, Handle, Suffix) -> - ?line RootDir = ?config(priv_dir,Config), - ?line FileName1 = atom_to_list(?MODULE)++"_rename"++Suffix++".fil", - ?line FileName2 = atom_to_list(?MODULE)++"_rename"++Suffix++".ful", - ?line Name1 = filename:join(RootDir, FileName1), - ?line Name2 = filename:join(RootDir, FileName2), - ?line {ok,Fd1} = ?PRIM_FILE:open(Name1, [write]), - ?line ok = ?PRIM_FILE:close(Fd1), +rename(Config) when is_list(Config) -> + RootDir = proplists:get_value(priv_dir,Config), + FileName1 = atom_to_list(?MODULE)++"_rename.fil", + FileName2 = atom_to_list(?MODULE)++"_rename.ful", + Name1 = filename:join(RootDir, FileName1), + Name2 = filename:join(RootDir, FileName2), + {ok,Fd1} = ?PRIM_FILE:open(Name1, [write]), + ok = ?PRIM_FILE:close(Fd1), %% Rename, and check that it really changed name - ?line ok = ?PRIM_FILE_call(rename, Handle, [Name1, Name2]), - ?line {error, _} = ?PRIM_FILE:open(Name1, [read]), - ?line {ok, Fd2} = ?PRIM_FILE:open(Name2, [read]), - ?line ok = ?PRIM_FILE:close(Fd2), + ok = ?PRIM_FILE:rename(Name1, Name2), + {error, _} = ?PRIM_FILE:open(Name1, [read]), + {ok, Fd2} = ?PRIM_FILE:open(Name2, [read]), + ok = ?PRIM_FILE:close(Fd2), %% Try renaming something to itself - ?line ok = ?PRIM_FILE_call(rename, Handle, [Name2, Name2]), + ok = ?PRIM_FILE:rename(Name2, Name2), %% Try renaming something that doesn't exist - ?line {error, enoent} = - ?PRIM_FILE_call(rename, Handle, [Name1, Name2]), + {error, enoent} = + ?PRIM_FILE:rename(Name1, Name2), %% Try renaming to something else than a string - ?line {error, badarg} = - ?PRIM_FILE_call(rename, Handle, [Name1, foobar]), - + {error, badarg} = + ?PRIM_FILE:rename(Name1, foobar), + %% Move between directories - ?line DirName1 = filename:join(RootDir, - atom_to_list(?MODULE) - ++"_rename_dir"++Suffix), - ?line DirName2 = filename:join(RootDir, - atom_to_list(?MODULE) - ++"_second_rename_dir"++Suffix), - ?line Name1foo = filename:join(DirName1, "foo.fil"), - ?line Name2foo = filename:join(DirName2, "foo.fil"), - ?line Name2bar = filename:join(DirName2, "bar.dir"), - ?line ok = ?PRIM_FILE:make_dir(DirName1), + DirName1 = filename:join(RootDir, + atom_to_list(?MODULE) + ++"_rename_dir"), + DirName2 = filename:join(RootDir, + atom_to_list(?MODULE) + ++"_second_rename_dir"), + Name1foo = filename:join(DirName1, "foo.fil"), + Name2foo = filename:join(DirName2, "foo.fil"), + Name2bar = filename:join(DirName2, "bar.dir"), + ok = ?PRIM_FILE:make_dir(DirName1), %% The name has to include the full file name, path is not enough - ?line expect( - {error, eexist}, {error, eisdir}, - ?PRIM_FILE_call(rename, Handle, [Name2, DirName1])), - ?line ok = - ?PRIM_FILE_call(rename, Handle, [Name2, Name1foo]), + expect( + {error, eexist}, {error, eisdir}, + ?PRIM_FILE:rename(Name2, DirName1)), + ok = + ?PRIM_FILE:rename(Name2, Name1foo), %% Now rename the directory - ?line ok = ?PRIM_FILE_call(rename, Handle, [DirName1, DirName2]), + ok = ?PRIM_FILE:rename(DirName1, DirName2), %% And check that the file is there now - ?line {ok,Fd3} = ?PRIM_FILE:open(Name2foo, [read]), - ?line ok = ?PRIM_FILE:close(Fd3), + {ok,Fd3} = ?PRIM_FILE:open(Name2foo, [read]), + ok = ?PRIM_FILE:close(Fd3), %% Try some dirty things now: move the directory into itself - ?line {error, Msg1} = - ?PRIM_FILE_call(rename, Handle, [DirName2, Name2bar]), - ?line io:format("Errmsg1: ~p",[Msg1]), + {error, Msg1} = + ?PRIM_FILE:rename(DirName2, Name2bar), + io:format("Errmsg1: ~p",[Msg1]), %% move dir into a file in itself - ?line {error, Msg2} = - ?PRIM_FILE_call(rename, Handle, [DirName2, Name2foo]), - ?line io:format("Errmsg2: ~p",[Msg2]), + {error, Msg2} = + ?PRIM_FILE:rename(DirName2, Name2foo), + io:format("Errmsg2: ~p",[Msg2]), ok. @@ -1508,45 +1274,41 @@ rename(Config, Handle, Suffix) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -e_delete(suite) -> []; -e_delete(doc) -> []; e_delete(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(10)), - ?line RootDir = ?config(priv_dir, Config), - ?line Base = filename:join(RootDir, - atom_to_list(?MODULE)++"_e_delete"), - ?line ok = ?PRIM_FILE:make_dir(Base), + RootDir = proplists:get_value(priv_dir, Config), + Base = filename:join(RootDir, + atom_to_list(?MODULE)++"_e_delete"), + ok = ?PRIM_FILE:make_dir(Base), %% Delete a non-existing file. - ?line {error, enoent} = + {error, enoent} = ?PRIM_FILE:delete(filename:join(Base, "non_existing")), %% Delete a directory. - ?line {error, eperm} = ?PRIM_FILE:delete(Base), + {error, eperm} = ?PRIM_FILE:delete(Base), %% Use a path-name with a non-directory component. - ?line Afile = filename:join(Base, "a_file"), - ?line ok = ?PRIM_FILE:write_file(Afile, "hello\n"), - ?line {error, E} = + Afile = filename:join(Base, "a_file"), + ok = ?PRIM_FILE:write_file(Afile, "hello\n"), + {error, E} = expect( {error, enotdir}, {error, enoent}, ?PRIM_FILE:delete(filename:join(Afile, "another_file"))), - ?line io:format("Result: ~p~n", [E]), + io:format("Result: ~p~n", [E]), %% No permission. - ?line case os:type() of - {win32, _} -> - %% Remove a character device. - ?line {error, eacces} = ?PRIM_FILE:delete("nul"); - _ -> - ?line ?PRIM_FILE:write_file_info( - Base, #file_info {mode=0}), - ?line {error, eacces} = ?PRIM_FILE:delete(Afile), - ?line ?PRIM_FILE:write_file_info( - Base, #file_info {mode=8#600}) - end, - - ?line test_server:timetrap_cancel(Dog), + case os:type() of + {win32, _} -> + %% Remove a character device. + {error, eacces} = ?PRIM_FILE:delete("nul"); + _ -> + ?PRIM_FILE:write_file_info( + Base, #file_info {mode=0}), + {error, eacces} = ?PRIM_FILE:delete(Afile), + ?PRIM_FILE:write_file_info( + Base, #file_info {mode=8#700}) + end, + ok. %%% FreeBSD gives EEXIST when renaming a file to an empty dir, although the @@ -1554,66 +1316,63 @@ e_delete(Config) when is_list(Config) -> %%% (What about FreeBSD? We store our nightly build results on a FreeBSD %%% file system, that's what.) -e_rename(suite) -> []; -e_rename(doc) -> []; e_rename(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(10)), - ?line RootDir = ?config(priv_dir, Config), - ?line Base = filename:join(RootDir, - atom_to_list(?MODULE)++"_e_rename"), - ?line ok = ?PRIM_FILE:make_dir(Base), + RootDir = proplists:get_value(priv_dir, Config), + Base = filename:join(RootDir, + atom_to_list(?MODULE)++"_e_rename"), + ok = ?PRIM_FILE:make_dir(Base), %% Create an empty directory. - ?line EmptyDir = filename:join(Base, "empty_dir"), - ?line ok = ?PRIM_FILE:make_dir(EmptyDir), + EmptyDir = filename:join(Base, "empty_dir"), + ok = ?PRIM_FILE:make_dir(EmptyDir), %% Create a non-empty directory. - ?line NonEmptyDir = filename:join(Base, "non_empty_dir"), - ?line ok = ?PRIM_FILE:make_dir(NonEmptyDir), - ?line ok = ?PRIM_FILE:write_file( - filename:join(NonEmptyDir, "a_file"), - "hello\n"), + NonEmptyDir = filename:join(Base, "non_empty_dir"), + ok = ?PRIM_FILE:make_dir(NonEmptyDir), + ok = ?PRIM_FILE:write_file( + filename:join(NonEmptyDir, "a_file"), + "hello\n"), %% Create another non-empty directory. - ?line ADirectory = filename:join(Base, "a_directory"), - ?line ok = ?PRIM_FILE:make_dir(ADirectory), - ?line ok = ?PRIM_FILE:write_file( - filename:join(ADirectory, "a_file"), - "howdy\n\n"), + ADirectory = filename:join(Base, "a_directory"), + ok = ?PRIM_FILE:make_dir(ADirectory), + ok = ?PRIM_FILE:write_file( + filename:join(ADirectory, "a_file"), + "howdy\n\n"), %% Create a data file. - ?line File = filename:join(Base, "just_a_file"), - ?line ok = ?PRIM_FILE:write_file(File, "anything goes\n\n"), + File = filename:join(Base, "just_a_file"), + ok = ?PRIM_FILE:write_file(File, "anything goes\n\n"), %% Move an existing directory to a non-empty directory. - ?line {error, eexist} = - ?PRIM_FILE:rename(ADirectory, NonEmptyDir), + {error, eexist} = + ?PRIM_FILE:rename(ADirectory, NonEmptyDir), %% Move a root directory. - ?line {error, einval} = ?PRIM_FILE:rename("/", "arne"), + {error, einval} = ?PRIM_FILE:rename("/", "arne"), %% Move Base into Base/new_name. - ?line {error, einval} = - ?PRIM_FILE:rename(Base, filename:join(Base, "new_name")), + {error, einval} = + ?PRIM_FILE:rename(Base, filename:join(Base, "new_name")), %% Overwrite a directory with a file. - ?line expect({error, eexist}, % FreeBSD (?) - {error, eisdir}, - ?PRIM_FILE:rename(File, EmptyDir)), - ?line expect({error, eexist}, % FreeBSD (?) - {error, eisdir}, - ?PRIM_FILE:rename(File, NonEmptyDir)), + expect({error, eexist}, % FreeBSD (?) + {error, eisdir}, + ?PRIM_FILE:rename(File, EmptyDir)), + expect({error, eexist}, % FreeBSD (?) + {error, eisdir}, + ?PRIM_FILE:rename(File, NonEmptyDir)), %% Move a non-existing file. - ?line NonExistingFile = filename:join( - Base, "non_existing_file"), - ?line {error, enoent} = - ?PRIM_FILE:rename(NonExistingFile, NonEmptyDir), + NonExistingFile = filename:join( + Base, "non_existing_file"), + {error, enoent} = + ?PRIM_FILE:rename(NonExistingFile, NonEmptyDir), %% Overwrite a file with a directory. - ?line expect({error, eexist}, % FreeBSD (?) - {error, enotdir}, - ?PRIM_FILE:rename(ADirectory, File)), + expect({error, eexist}, % FreeBSD (?) + {error, enotdir}, + ?PRIM_FILE:rename(ADirectory, File)), %% Move a file to another filesystem. %% XXX - This test case is bogus. We cannot be guaranteed that @@ -1621,315 +1380,136 @@ e_rename(Config) when is_list(Config) -> %% different filesystems. %% %% XXX - Gross hack! - ?line Comment = - case os:type() of - {win32, _} -> - %% At least Windows NT can - %% successfully move a file to - %% another drive. - ok; - {unix, _ } -> - OtherFs = "/tmp", - ?line NameOnOtherFs = - filename:join(OtherFs, - filename:basename(File)), - ?line {ok, Com} = - case ?PRIM_FILE:rename( - File, NameOnOtherFs) of - {error, exdev} -> - %% The file could be in - %% the same filesystem! - {ok, ok}; - ok -> - {ok, {comment, - "Moving between filesystems " - "suceeded, files are probably " - "in the same filesystem!"}}; - {error, eperm} -> - {ok, {comment, "SBS! You don't " - "have the permission to do " - "this test!"}}; - Else -> - Else - end, - Com; - {ose, _} -> - %% disabled for now - ok - end, - ?line test_server:timetrap_cancel(Dog), + Comment = + case os:type() of + {win32, _} -> + %% At least Windows NT can + %% successfully move a file to + %% another drive. + ok; + _ -> + OtherFs = "/tmp", + NameOnOtherFs = + filename:join(OtherFs, + filename:basename(File)), + {ok, Com} = + case ?PRIM_FILE:rename( + File, NameOnOtherFs) of + {error, exdev} -> + %% The file could be in + %% the same filesystem! + {ok, ok}; + ok -> + {ok, {comment, + "Moving between filesystems " + "suceeded, files are probably " + "in the same filesystem!"}}; + {error, eperm} -> + {ok, {comment, "SBS! You don't " + "have the permission to do " + "this test!"}}; + Else -> + Else + end, + Com + end, Comment. -e_make_dir(suite) -> []; -e_make_dir(doc) -> []; e_make_dir(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(10)), - ?line RootDir = ?config(priv_dir, Config), - ?line Base = filename:join(RootDir, - atom_to_list(?MODULE)++"_e_make_dir"), - ?line ok = ?PRIM_FILE:make_dir(Base), + RootDir = proplists:get_value(priv_dir, Config), + Base = filename:join(RootDir, + atom_to_list(?MODULE)++"_e_make_dir"), + ok = ?PRIM_FILE:make_dir(Base), %% A component of the path does not exist. - ?line {error, enoent} = + {error, enoent} = ?PRIM_FILE:make_dir(filename:join([Base, "a", "b"])), %% Use a path-name with a non-directory component. - ?line Afile = filename:join(Base, "a_directory"), - ?line ok = ?PRIM_FILE:write_file(Afile, "hello\n"), - ?line case ?PRIM_FILE:make_dir( - filename:join(Afile, "another_directory")) of - {error, enotdir} -> io:format("Result: enotdir"); - {error, enoent} -> io:format("Result: enoent") - end, + Afile = filename:join(Base, "a_directory"), + ok = ?PRIM_FILE:write_file(Afile, "hello\n"), + case ?PRIM_FILE:make_dir( + filename:join(Afile, "another_directory")) of + {error, enotdir} -> io:format("Result: enotdir"); + {error, enoent} -> io:format("Result: enoent") + end, %% No permission (on Unix only). case os:type() of {win32, _} -> ok; _ -> - ?line ?PRIM_FILE:write_file_info(Base, #file_info {mode=0}), - ?line {error, eacces} = + ?PRIM_FILE:write_file_info(Base, #file_info {mode=0}), + {error, eacces} = ?PRIM_FILE:make_dir(filename:join(Base, "xxxx")), - ?line - ?PRIM_FILE:write_file_info(Base, #file_info {mode=8#600}) + ?PRIM_FILE:write_file_info(Base, #file_info {mode=8#700}) end, - ?line test_server:timetrap_cancel(Dog), ok. -e_del_dir(suite) -> []; -e_del_dir(doc) -> []; e_del_dir(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(10)), - ?line RootDir = ?config(priv_dir, Config), - ?line Base = filename:join(RootDir, - atom_to_list(?MODULE)++"_e_del_dir"), - ?line io:format("Base: ~p", [Base]), - ?line ok = ?PRIM_FILE:make_dir(Base), + RootDir = proplists:get_value(priv_dir, Config), + Base = filename:join(RootDir, + atom_to_list(?MODULE)++"_e_del_dir"), + io:format("Base: ~p", [Base]), + ok = ?PRIM_FILE:make_dir(Base), %% Delete a non-existent directory. - ?line {error, enoent} = + {error, enoent} = ?PRIM_FILE:del_dir(filename:join(Base, "non_existing")), %% Use a path-name with a non-directory component. - ?line Afile = filename:join(Base, "a_directory"), - ?line ok = ?PRIM_FILE:write_file(Afile, "hello\n"), - ?line {error, E1} = + Afile = filename:join(Base, "a_directory"), + ok = ?PRIM_FILE:write_file(Afile, "hello\n"), + {error, E1} = expect({error, enotdir}, {error, enoent}, ?PRIM_FILE:del_dir( filename:join(Afile, "another_directory"))), - ?line io:format("Result: ~p", [E1]), + io:format("Result: ~p", [E1]), %% Delete a non-empty directory. %% Delete a non-empty directory. - ?line {error, E2} = + {error, E2} = expect({error, enotempty}, {error, eexist}, {error, eacces}, ?PRIM_FILE:del_dir(Base)), - ?line io:format("Result: ~p", [E2]), + io:format("Result: ~p", [E2]), %% Remove the current directory. - ?line {error, E3} = + {error, E3} = expect({error, einval}, {error, eperm}, % Linux and DUX {error, eacces}, {error, ebusy}, ?PRIM_FILE:del_dir(".")), - ?line io:format("Result: ~p", [E3]), + io:format("Result: ~p", [E3]), %% No permission. case os:type() of {win32, _} -> ok; _ -> - ?line ADirectory = filename:join(Base, "no_perm"), - ?line ok = ?PRIM_FILE:make_dir(ADirectory), - ?line ?PRIM_FILE:write_file_info(Base, #file_info {mode=0}), - ?line {error, eacces} = ?PRIM_FILE:del_dir(ADirectory), - ?line ?PRIM_FILE:write_file_info( - Base, #file_info {mode=8#600}) - end, - ?line test_server:timetrap_cancel(Dog), - ok. - - -%% Trying reading and positioning from a compressed file. - -read_compressed(suite) -> []; -read_compressed(doc) -> []; -read_compressed(Config) when is_list(Config) -> - ?line Data = ?config(data_dir, Config), - ?line Real = filename:join(Data, "realmen.html.gz"), - ?line {ok, Fd} = ?PRIM_FILE:open(Real, [read, compressed]), - ?line try_read_file(Fd). - -%% Trying reading and positioning from an uncompressed file, -%% but with the compressed flag given. - -read_not_really_compressed(suite) -> []; -read_not_really_compressed(doc) -> []; -read_not_really_compressed(Config) when is_list(Config) -> - ?line Data = ?config(data_dir, Config), - ?line Priv = ?config(priv_dir, Config), - - %% The file realmen.html might have got CRs added (by WinZip). - %% Remove them, or the file positions will not be correct. - - ?line Real = filename:join(Data, "realmen.html"), - ?line RealPriv = filename:join(Priv, - atom_to_list(?MODULE)++"_realmen.html"), - ?line {ok, RealDataBin} = ?PRIM_FILE:read_file(Real), - ?line RealData = remove_crs(binary_to_list(RealDataBin), []), - ?line ok = ?PRIM_FILE:write_file(RealPriv, RealData), - ?line {ok, Fd} = ?PRIM_FILE:open(RealPriv, [read, compressed]), - ?line try_read_file(Fd). - -remove_crs([$\r|Rest], Result) -> - remove_crs(Rest, Result); -remove_crs([C|Rest], Result) -> - remove_crs(Rest, [C|Result]); -remove_crs([], Result) -> - lists:reverse(Result). - -try_read_file(Fd) -> - ?line Dog = test_server:timetrap(test_server:seconds(10)), - - %% Seek to the current position (nothing should happen). - - ?line {ok, 0} = ?PRIM_FILE:position(Fd, 0), - ?line {ok, 0} = ?PRIM_FILE:position(Fd, {cur, 0}), - - %% Read a few lines from a compressed file. - - ?line ShouldBe = "<TITLE>Real Programmers Don't Use PASCAL</TITLE>\n", - ?line {ok, ShouldBe} = ?PRIM_FILE:read(Fd, length(ShouldBe)), - - %% Now seek forward. - - ?line {ok, 381} = ?PRIM_FILE:position(Fd, 381), - ?line Back = "Back in the good old days -- the \"Golden Era\" " ++ - "of computers, it was\n", - ?line {ok, Back} = ?PRIM_FILE:read(Fd, length(Back)), - - %% Try to search forward relative to the current position. - - ?line {ok, CurPos} = ?PRIM_FILE:position(Fd, {cur, 0}), - ?line RealPos = 4273, - ?line {ok, RealPos} = ?PRIM_FILE:position(Fd, {cur, RealPos-CurPos}), - ?line RealProg = "<LI> Real Programmers aren't afraid to use GOTOs.\n", - ?line {ok, RealProg} = ?PRIM_FILE:read(Fd, length(RealProg)), - - %% Seek backward. - - ?line AfterTitle = length("<TITLE>"), - ?line {ok, AfterTitle} = ?PRIM_FILE:position(Fd, AfterTitle), - ?line Title = "Real Programmers Don't Use PASCAL</TITLE>\n", - ?line {ok, Title} = ?PRIM_FILE:read(Fd, length(Title)), - - %% Done. - - ?line ?PRIM_FILE:close(Fd), - ?line test_server:timetrap_cancel(Dog), - ok. - -write_compressed(suite) -> []; -write_compressed(doc) -> []; -write_compressed(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(10)), - ?line Priv = ?config(priv_dir, Config), - ?line MyFile = filename:join(Priv, - atom_to_list(?MODULE)++"_test.gz"), - - %% Write a file. - - ?line {ok, Fd} = ?PRIM_FILE:open(MyFile, [write, compressed]), - ?line {ok, 0} = ?PRIM_FILE:position(Fd, 0), - ?line Prefix = "hello\n", - ?line End = "end\n", - ?line ok = ?PRIM_FILE:write(Fd, Prefix), - ?line {ok, 143} = ?PRIM_FILE:position(Fd, 143), - ?line ok = ?PRIM_FILE:write(Fd, End), - ?line ok = ?PRIM_FILE:close(Fd), - - %% Read the file and verify the contents. - - ?line {ok, Fd1} = ?PRIM_FILE:open(MyFile, [read, compressed]), - ?line {ok, Prefix} = ?PRIM_FILE:read(Fd1, length(Prefix)), - ?line Second = lists:duplicate(143-length(Prefix), 0) ++ End, - ?line {ok, Second} = ?PRIM_FILE:read(Fd1, length(Second)), - ?line ok = ?PRIM_FILE:close(Fd1), - - %% Ensure that the file is compressed. - - TotalSize = 143 + length(End), - case ?PRIM_FILE:read_file_info(MyFile) of - {ok, #file_info{size=Size}} when Size < TotalSize -> - ok; - {ok, #file_info{size=Size}} when Size == TotalSize -> - test_server:fail(file_not_compressed) + ADirectory = filename:join(Base, "no_perm"), + ok = ?PRIM_FILE:make_dir(ADirectory), + ?PRIM_FILE:write_file_info(Base, #file_info {mode=0}), + {error, eacces} = ?PRIM_FILE:del_dir(ADirectory), + ?PRIM_FILE:write_file_info( + Base, #file_info {mode=8#700}) end, - - %% Write again to ensure that the file is truncated. - - ?line {ok, Fd2} = ?PRIM_FILE:open(MyFile, [write, compressed]), - ?line NewString = "aaaaaaaaaaa", - ?line ok = ?PRIM_FILE:write(Fd2, NewString), - ?line ok = ?PRIM_FILE:close(Fd2), - ?line {ok, Fd3} = ?PRIM_FILE:open(MyFile, [read, compressed]), - ?line {ok, NewString} = ?PRIM_FILE:read(Fd3, 1024), - ?line ok = ?PRIM_FILE:close(Fd3), - - %% Done. - - ?line test_server:timetrap_cancel(Dog), - ok. - -compress_errors(suite) -> []; -compress_errors(doc) -> []; -compress_errors(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(10)), - ?line Data = ?config(data_dir, Config), - ?line {error, enoent} = ?PRIM_FILE:open("non_existing__", - [compressed, read]), - ?line {error, einval} = ?PRIM_FILE:open("non_existing__", - [compressed, read, write]), - - %% Read a corrupted .gz file. - - ?line Corrupted = filename:join(Data, "corrupted.gz"), - ?line {ok, Fd} = ?PRIM_FILE:open(Corrupted, [read, compressed]), - ?line {error, eio} = ?PRIM_FILE:read(Fd, 100), - ?line ?PRIM_FILE:close(Fd), - - ?line test_server:timetrap_cancel(Dog), ok. -make_link_a(doc) -> "Test creating a hard link."; -make_link_a(suite) -> []; -make_link_a(Config) when is_list(Config) -> - make_link(Config, [], "_a"). +make_link(Config) when is_list(Config) -> + RootDir = proplists:get_value(priv_dir, Config), + NewDir = filename:join(RootDir, + atom_to_list(?MODULE) + ++"_make_link"), + ok = ?PRIM_FILE:make_dir(NewDir), -make_link_b(doc) -> "Test creating a hard link."; -make_link_b(suite) -> []; -make_link_b(Config) when is_list(Config) -> - ?line {ok, Handle} = ?PRIM_FILE:start(), - Result = make_link(Config, Handle, "_b"), - ?line ok = ?PRIM_FILE:stop(Handle), - Result. + Name = filename:join(NewDir, "a_file"), + ok = ?PRIM_FILE:write_file(Name, "some contents\n"), -make_link(Config, Handle, Suffix) -> - ?line Dog = test_server:timetrap(test_server:seconds(10)), - ?line RootDir = ?config(priv_dir, Config), - ?line NewDir = filename:join(RootDir, - atom_to_list(?MODULE) - ++"_make_link"++Suffix), - ?line ok = ?PRIM_FILE_call(make_dir, Handle, [NewDir]), - - ?line Name = filename:join(NewDir, "a_file"), - ?line ok = ?PRIM_FILE:write_file(Name, "some contents\n"), - - ?line Alias = filename:join(NewDir, "an_alias"), - ?line Result = - case ?PRIM_FILE_call(make_link, Handle, [Name, Alias]) of + Alias = filename:join(NewDir, "an_alias"), + Result = + case ?PRIM_FILE:make_link(Name, Alias) of {error, enotsup} -> {skipped, "Links not supported on this platform"}; ok -> @@ -1938,133 +1518,102 @@ make_link(Config, Handle, Suffix) -> %% which should in behave exactly as %% ?PRIM_FILE:read_file_info/1 %% since they are not used on symbolic links. - - ?line {ok, Info} = - ?PRIM_FILE_call(read_link_info, Handle, [Name]), - ?line {ok, Info} = - ?PRIM_FILE_call(read_link_info, Handle, [Alias]), - ?line #file_info{links = 2, type = regular} = Info, - ?line {error, eexist} = - ?PRIM_FILE_call(make_link, Handle, [Name, Alias]), + + {ok, Info} = + ?PRIM_FILE:read_link_info(Name), + {ok, Info} = + ?PRIM_FILE:read_link_info(Alias), + #file_info{links = 2, type = regular} = Info, + {error, eexist} = + ?PRIM_FILE:make_link(Name, Alias), ok end, - - ?line test_server:timetrap_cancel(Dog), + Result. -read_link_info_for_non_link(doc) -> - "Test that reading link info for an ordinary file or directory works " - "(on all platforms)."; -read_link_info_for_non_link(suite) -> []; +%% Test that reading link info for an ordinary file or directory works +%% (on all platforms). read_link_info_for_non_link(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(10)), - - ?line {ok, #file_info{type=directory}} = ?PRIM_FILE:read_link_info("."), - - ?line test_server:timetrap_cancel(Dog), + {ok, #file_info{type=directory}} = ?PRIM_FILE:read_link_info("."), ok. - -symlinks_a(doc) -> "Test operations on symbolic links (for Unix)."; -symlinks_a(suite) -> []; -symlinks_a(Config) when is_list(Config) -> - symlinks(Config, [], "_a"). - -symlinks_b(doc) -> "Test operations on symbolic links (for Unix)."; -symlinks_b(suite) -> []; -symlinks_b(Config) when is_list(Config) -> - ?line {ok, Handle} = ?PRIM_FILE:start(), - Result = symlinks(Config, Handle, "_b"), - ?line ok = ?PRIM_FILE:stop(Handle), - Result. -symlinks(Config, Handle, Suffix) -> - ?line Dog = test_server:timetrap(test_server:seconds(10)), - ?line RootDir = ?config(priv_dir, Config), - ?line NewDir = filename:join(RootDir, - atom_to_list(?MODULE) - ++"_make_symlink"++Suffix), - ?line ok = ?PRIM_FILE_call(make_dir, Handle, [NewDir]), - - ?line Name = filename:join(NewDir, "a_plain_file"), - ?line ok = ?PRIM_FILE:write_file(Name, "some stupid content\n"), - - ?line Alias = filename:join(NewDir, "a_symlink_alias"), - ?line Result = - case ?PRIM_FILE_call(make_symlink, Handle, [Name, Alias]) of +symlinks(Config) when is_list(Config) -> + RootDir = proplists:get_value(priv_dir, Config), + NewDir = filename:join(RootDir, + atom_to_list(?MODULE) + ++"_make_symlink"), + ok = ?PRIM_FILE:make_dir(NewDir), + + Name = filename:join(NewDir, "a_plain_file"), + ok = ?PRIM_FILE:write_file(Name, "some stupid content\n"), + + Alias = filename:join(NewDir, "a_symlink_alias"), + Result = + case ?PRIM_FILE:make_symlink(Name, Alias) of {error, enotsup} -> {skipped, "Links not supported on this platform"}; {error, eperm} -> {win32,_} = os:type(), {skipped, "Windows user not privileged to create links"}; ok -> - ?line {ok, Info1} = - ?PRIM_FILE_call(read_file_info, Handle, [Name]), - ?line {ok, Info1} = - ?PRIM_FILE_call(read_file_info, Handle, [Alias]), - ?line {ok, Info1} = - ?PRIM_FILE_call(read_link_info, Handle, [Name]), - ?line #file_info{links = 1, type = regular} = Info1, - - ?line {ok, Info2} = - ?PRIM_FILE_call(read_link_info, Handle, [Alias]), - ?line #file_info{links=1, type=symlink} = Info2, - ?line {ok, Name} = - ?PRIM_FILE_call(read_link, Handle, [Alias]), + {ok, Info1} = + ?PRIM_FILE:read_file_info(Name), + {ok, Info1} = + ?PRIM_FILE:read_file_info(Alias), + {ok, Info1} = + ?PRIM_FILE:read_link_info(Name), + #file_info{links = 1, type = regular} = Info1, + + {ok, Info2} = + ?PRIM_FILE:read_link_info(Alias), + #file_info{links=1, type=symlink} = Info2, + {ok, Name} = + ?PRIM_FILE:read_link(Alias), {ok, Name} = - ?PRIM_FILE_call(read_link_all, Handle, [Alias]), + ?PRIM_FILE:read_link_all(Alias), %% If all is good, delete dir again (avoid hanging dir on windows) rm_rf(?PRIM_FILE,NewDir), ok end, - - ?line test_server:timetrap_cancel(Dog), + Result. %% Creates as many files as possible during a certain time, %% periodically calls list_dir/2 to check if it works, %% then deletes all files. -list_dir_limit(doc) -> - "Tests if large directories can be read"; -list_dir_limit(suite) -> - []; +%% Tests if large directories can be read. list_dir_limit(Config) when is_list(Config) -> - ?line MaxTime = 120, - ?line MaxNumber = 20000, - ?line Dog = test_server:timetrap( - test_server:seconds(2*MaxTime + MaxTime)), - ?line RootDir = ?config(priv_dir, Config), - ?line NewDir = filename:join(RootDir, - atom_to_list(?MODULE)++"_list_dir_limit"), - ?line {ok, Handle1} = ?PRIM_FILE:start(), - ?line ok = ?PRIM_FILE_call(make_dir, Handle1, [NewDir]), + MaxTime = 120, + MaxNumber = 20000, + ct:timetrap({seconds,2*MaxTime + MaxTime}), + RootDir = proplists:get_value(priv_dir, Config), + NewDir = filename:join(RootDir, + atom_to_list(?MODULE)++"_list_dir_limit"), + ok = ?PRIM_FILE:make_dir(NewDir), Ref = erlang:start_timer(MaxTime*1000, self(), []), - ?line Result = list_dir_limit_loop(NewDir, Handle1, Ref, MaxNumber, 0), - ?line Time = case erlang:cancel_timer(Ref) of - false -> MaxTime; - T -> MaxTime - (T div 1000) - end, - ?line Number = case Result of - {ok, N} -> N; - {error, _Reason, N} -> N; - _ -> 0 - end, - ?line {ok, Handle2} = ?PRIM_FILE:start(), - ?line list_dir_limit_cleanup(NewDir, Handle2, Number, 0), - ?line ok = ?PRIM_FILE:stop(Handle1), - ?line ok = ?PRIM_FILE:stop(Handle2), - ?line {ok, Number} = Result, - ?line test_server:timetrap_cancel(Dog), + Result = list_dir_limit_loop(NewDir, Ref, MaxNumber, 0), + Time = case erlang:cancel_timer(Ref) of + false -> MaxTime; + T -> MaxTime - (T div 1000) + end, + Number = case Result of + {ok, N} -> N; + {error, _Reason, N} -> N; + _ -> 0 + end, + list_dir_limit_cleanup(NewDir, Number, 0), + {ok, Number} = Result, {comment, "Created " ++ integer_to_list(Number) ++ " files in " ++ integer_to_list(Time) ++ " seconds."}. -list_dir_limit_loop(Dir, Handle, _Ref, N, Cnt) when Cnt >= N -> - list_dir_check(Dir, Handle, Cnt); -list_dir_limit_loop(Dir, Handle, Ref, N, Cnt) -> +list_dir_limit_loop(Dir, _Ref, N, Cnt) when Cnt >= N -> + list_dir_check(Dir, Cnt); +list_dir_limit_loop(Dir, Ref, N, Cnt) -> receive {timeout, Ref, []} -> - list_dir_check(Dir, Handle, Cnt) + list_dir_check(Dir, Cnt) after 0 -> Name = integer_to_list(Cnt), case ?PRIM_FILE:write_file(filename:join(Dir, Name), Name) of @@ -2072,23 +1621,23 @@ list_dir_limit_loop(Dir, Handle, Ref, N, Cnt) -> Next = Cnt + 1, case Cnt rem 100 of 0 -> - case list_dir_check(Dir, Handle, Next) of + case list_dir_check(Dir, Next) of {ok, Next} -> list_dir_limit_loop( - Dir, Handle, Ref, N, Next); + Dir, Ref, N, Next); Other -> Other end; _ -> - list_dir_limit_loop(Dir, Handle, Ref, N, Next) + list_dir_limit_loop(Dir, Ref, N, Next) end; {error, Reason} -> {error, Reason, Cnt} end end. -list_dir_check(Dir, Handle, Cnt) -> - case ?PRIM_FILE:list_dir(Handle, Dir) of +list_dir_check(Dir, Cnt) -> + case ?PRIM_FILE:list_dir(Dir) of {ok, ListDir} -> case length(ListDir) of Cnt -> @@ -2105,27 +1654,27 @@ list_dir_check(Dir, Handle, Cnt) -> %% Deletes N files while ignoring errors, then continues deleting %% as long as they exist. -list_dir_limit_cleanup(Dir, Handle, N, Cnt) when Cnt >= N -> +list_dir_limit_cleanup(Dir, N, Cnt) when Cnt >= N -> Name = integer_to_list(Cnt), - case ?PRIM_FILE:delete(Handle, filename:join(Dir, Name)) of + case ?PRIM_FILE:delete(filename:join(Dir, Name)) of ok -> - list_dir_limit_cleanup(Dir, Handle, N, Cnt+1); + list_dir_limit_cleanup(Dir, N, Cnt+1); _ -> ok end; -list_dir_limit_cleanup(Dir, Handle, N, Cnt) -> +list_dir_limit_cleanup(Dir, N, Cnt) -> Name = integer_to_list(Cnt), - ?PRIM_FILE:delete(Handle, filename:join(Dir, Name)), - list_dir_limit_cleanup(Dir, Handle, N, Cnt+1). + ?PRIM_FILE:delete(filename:join(Dir, Name)), + list_dir_limit_cleanup(Dir, N, Cnt+1). %%% %%% Test list_dir() on a non-existing pathname. %%% list_dir_error(Config) -> - Priv = ?config(priv_dir, Config), + Priv = proplists:get_value(priv_dir, Config), NonExisting = filename:join(Priv, "non-existing-dir"), - {error,enoent} = prim_file:list_dir(NonExisting), + {error,enoent} = ?PRIM_FILE:list_dir(NonExisting), ok. %%% @@ -2133,7 +1682,7 @@ list_dir_error(Config) -> %%% list_dir(Config) -> - RootDir = ?config(priv_dir, Config), + RootDir = proplists:get_value(priv_dir, Config), TestDir = filename:join(RootDir, ?MODULE_STRING++"_list_dir"), ?PRIM_FILE:make_dir(TestDir), list_dir_1(TestDir, 42, []). @@ -2164,11 +1713,13 @@ run_large_file_test(Config, Run, Name) -> {{unix,sunos},OsVersion} when OsVersion < {5,5,1} -> {skip,"Only supported on Win32, Unix or SunOS >= 5.5.1"}; {{unix,_},_} -> - N = unix_free(?config(priv_dir, Config)), - io:format("Free disk: ~w KByte~n", [N]), - if N < 5 bsl 20 -> + DiscFree = unix_free(proplists:get_value(priv_dir, Config)), + MemFree = free_memory(), + io:format("Free disk: ~w KByte~n", [DiscFree]), + io:format("Free mem: ~w MByte~n", [MemFree]), + if DiscFree < 5 bsl 20; MemFree < 5 bsl 10 -> %% Less than 5 GByte free - {skip,"Less than 5 GByte free disk"}; + {skip,"Less than 5 GByte free disk/mem"}; true -> do_run_large_file_test(Config, Run, Name) end; @@ -2178,9 +1729,9 @@ run_large_file_test(Config, Run, Name) -> do_run_large_file_test(Config, Run, Name0) -> - Name = filename:join(?config(priv_dir, Config), + Name = filename:join(proplists:get_value(priv_dir, Config), ?MODULE_STRING ++ Name0), - + %% Set up a process that will delete this file. Tester = self(), Deleter = @@ -2191,9 +1742,9 @@ do_run_large_file_test(Config, Run, Name0) -> {'DOWN',Mref,_,_,_} -> ok; {Tester,done} -> ok end, - prim_file:delete(Name) + ?PRIM_FILE:delete(Name) end), - + %% Run the test case. Res = Run(Name), @@ -2221,6 +1772,40 @@ zip_data([], Bs) -> zip_data(As, []) -> As. +%% Stolen from emulator -> alloc_SUITE +free_memory() -> + %% Free memory in MB. + try + SMD = memsup:get_system_memory_data(), + {value, {free_memory, Free}} = lists:keysearch(free_memory, 1, SMD), + TotFree = (Free + + case lists:keysearch(cached_memory, 1, SMD) of + {value, {cached_memory, Cached}} -> Cached; + false -> 0 + end + + case lists:keysearch(buffered_memory, 1, SMD) of + {value, {buffered_memory, Buffed}} -> Buffed; + false -> 0 + end), + usable_mem(TotFree) div (1024*1024) + catch + error : undef -> + ct:fail({"os_mon not built"}) + end. + +usable_mem(Memory) -> + case test_server:is_valgrind() of + true -> + %% Valgrind uses extra memory for the V- and A-bits. + %% http://valgrind.org/docs/manual/mc-manual.html#mc-manual.value + %% Docs says it uses "compression to represent the V bits compactly" + %% but let's be conservative and cut usable memory in half. + Memory div 2; + false -> + Memory + end. + + %%%----------------------------------------------------------------- %%% Utilities rm_rf(Mod,Dir) -> diff --git a/lib/kernel/test/ram_file_SUITE.erl b/lib/kernel/test/ram_file_SUITE.erl index 615251a257..b0265393bd 100644 --- a/lib/kernel/test/ram_file_SUITE.erl +++ b/lib/kernel/test/ram_file_SUITE.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2013. All Rights Reserved. +%% Copyright Ericsson AB 2001-2016. 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/. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at %% -%% 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. +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% @@ -25,9 +26,10 @@ init_per_testcase/2, end_per_testcase/2]). -export([open_modes/1, open_old_modes/1, pread_pwrite/1, position/1, truncate/1, sync/1, get_set_file/1, compress/1, uuencode/1, - large_file_errors/1, large_file_light/1, large_file_heavy/1]). + large_file_errors/1, large_file_light/1, + large_file_heavy/0, large_file_heavy/1]). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -include_lib("kernel/include/file.hrl"). -define(FILE_MODULE, file). % Name of module to test @@ -35,7 +37,9 @@ %%-------------------------------------------------------------------------- -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap,{minutes,1}}]. all() -> [open_modes, open_old_modes, pread_pwrite, position, @@ -58,37 +62,23 @@ end_per_group(_GroupName, Config) -> Config. -init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) -> - Time = - case Func of - large_file_heavy -> - ?t:minutes(5); - _ -> - ?t:seconds(10) - end, - Dog = ?t:timetrap(Time), - %% error_logger:info_msg("~p:~p *****~n", [?MODULE, Func]), - [{watchdog, Dog} | Config]. +init_per_testcase(Func, Config) -> + Config. end_per_testcase(_Func, Config) -> - %% error_logger:info_msg("~p:~p END *****~n", [?MODULE, Func]), - Dog = ?config(watchdog, Config), - ?t:timetrap_cancel(Dog). + Config. %%-------------------------------------------------------------------------- %% Test suites -open_modes(suite) -> - []; -open_modes(doc) -> - ["Test that the basic read, write and binary options works for open/2."]; +%% Test that the basic read, write and binary options works for open/2. open_modes(Config) when is_list(Config) -> - ?line Str1 = "The quick brown fox ", - ?line Str2 = "jumps over a lazy dog ", - ?line Str = Str1 ++ Str2, - ?line Bin1 = list_to_binary(Str1), - ?line Bin2 = list_to_binary(Str2), - ?line Bin = list_to_binary(Str), + Str1 = "The quick brown fox ", + Str2 = "jumps over a lazy dog ", + Str = Str1 ++ Str2, + Bin1 = list_to_binary(Str1), + Bin2 = list_to_binary(Str2), + Bin = list_to_binary(Str), %% open_read_write(?FILE_MODULE, Str1, [ram, read, write], Str2), open_read(?FILE_MODULE, Str, [ram]), @@ -97,18 +87,15 @@ open_modes(Config) when is_list(Config) -> %% ok. -open_old_modes(suite) -> - []; -open_old_modes(doc) -> - ["Test that the old style read, write and binary options ", - "works for open/2."]; +%% Test that the old style read, write and binary options +%% works for open/2. open_old_modes(Config) when is_list(Config) -> - ?line Str1 = "The quick brown fox ", - ?line Str2 = "jumps over a lazy dog ", - ?line Str = Str1 ++ Str2, - ?line Bin1 = list_to_binary(Str1), - ?line Bin2 = list_to_binary(Str2), - ?line Bin = list_to_binary(Str), + Str1 = "The quick brown fox ", + Str2 = "jumps over a lazy dog ", + Str = Str1 ++ Str2, + Bin1 = list_to_binary(Str1), + Bin2 = list_to_binary(Str2), + Bin = list_to_binary(Str), %% open_read_write(?RAM_FILE_MODULE, Str1, read_write, Str2), open_read(?RAM_FILE_MODULE, Str, read), @@ -118,57 +105,54 @@ open_old_modes(Config) when is_list(Config) -> ok. open_read_write(Module, Data1, Options, Data2) -> - ?line io:format("~p:open_read_write(~p, ~p, ~p, ~p)~n", - [?MODULE, Module, Data1, Options, Data2]), - %% - ?line Size1 = sizeof(Data1), - ?line Size2 = sizeof(Data2), - ?line Data = append(Data1, Data2), - ?line Size = Size1 + Size2, - %% - ?line {ok, Fd} = Module:open(Data1, Options), - ?line {ok, Data1} = Module:read(Fd, Size1), - ?line eof = Module:read(Fd, 1), - ?line {ok, Zero} = Module:read(Fd, 0), - ?line 0 = sizeof(Zero), - ?line ok = Module:write(Fd, Data2), - ?line {ok, 0} = Module:position(Fd, bof), - ?line {ok, Data} = Module:read(Fd, Size), - ?line eof = Module:read(Fd, 1), - ?line {ok, Zero} = Module:read(Fd, 0), - ?line ok = Module:close(Fd), - %% - ?line ok. + io:format("~p:open_read_write(~p, ~p, ~p, ~p)~n", + [?MODULE, Module, Data1, Options, Data2]), + %% + Size1 = sizeof(Data1), + Size2 = sizeof(Data2), + Data = append(Data1, Data2), + Size = Size1 + Size2, + %% + {ok, Fd} = Module:open(Data1, Options), + {ok, Data1} = Module:read(Fd, Size1), + eof = Module:read(Fd, 1), + {ok, Zero} = Module:read(Fd, 0), + 0 = sizeof(Zero), + ok = Module:write(Fd, Data2), + {ok, 0} = Module:position(Fd, bof), + {ok, Data} = Module:read(Fd, Size), + eof = Module:read(Fd, 1), + {ok, Zero} = Module:read(Fd, 0), + ok = Module:close(Fd), + %% + ok. open_read(Module, Data, Options) -> - ?line io:format("~p:open_read(~p, ~p, ~p)~n", - [?MODULE, Module, Data, Options]), - %% - ?line Size = sizeof(Data), - %% - ?line {ok, Fd} = Module:open(Data, Options), - ?line {ok, Data} = Module:read(Fd, Size), - ?line eof = Module:read(Fd, 1), - ?line {ok, Zero} = Module:read(Fd, 0), - ?line 0 = sizeof(Zero), - ?line {error, ebadf} = Module:write(Fd, Data), - ?line {ok, 0} = Module:position(Fd, bof), - ?line {ok, Data} = Module:read(Fd, Size), - ?line eof = Module:read(Fd, 1), - ?line {ok, Zero} = Module:read(Fd, 0), - ?line ok = Module:close(Fd), + io:format("~p:open_read(~p, ~p, ~p)~n", + [?MODULE, Module, Data, Options]), + %% + Size = sizeof(Data), + %% + {ok, Fd} = Module:open(Data, Options), + {ok, Data} = Module:read(Fd, Size), + eof = Module:read(Fd, 1), + {ok, Zero} = Module:read(Fd, 0), + 0 = sizeof(Zero), + {error, ebadf} = Module:write(Fd, Data), + {ok, 0} = Module:position(Fd, bof), + {ok, Data} = Module:read(Fd, Size), + eof = Module:read(Fd, 1), + {ok, Zero} = Module:read(Fd, 0), + ok = Module:close(Fd), %% - ?line ok. + ok. -pread_pwrite(suite) -> - []; -pread_pwrite(doc) -> - ["Test that pread/2,3 and pwrite/2,3 works."]; +%% Test that pread/2,3 and pwrite/2,3 works. pread_pwrite(Config) when is_list(Config) -> - ?line Str = "Flygande bäckaziner söka hwila på mjuqa tuvor x", - ?line Bin = list_to_binary(Str), + Str = "Flygande bäckaziner söka hwila på mjuqa tuvor x", + Bin = list_to_binary(Str), %% pread_pwrite_test(?FILE_MODULE, Str, [ram, read, write]), pread_pwrite_test(?FILE_MODULE, Bin, [ram, binary, read, write]), @@ -178,36 +162,33 @@ pread_pwrite(Config) when is_list(Config) -> ok. pread_pwrite_test(Module, Data, Options) -> - ?line io:format("~p:pread_pwrite_test(~p, ~p, ~p)~n", - [?MODULE, Module, Data, Options]), - %% - ?line Size = sizeof(Data), - %% - ?line {ok, Fd} = Module:open([], Options), - ?line ok = Module:pwrite(Fd, 0, Data), - ?line {ok, Data} = Module:pread(Fd, 0, Size+1), - ?line eof = Module:pread(Fd, Size+1, 1), - ?line {ok, Zero} = Module:pread(Fd, Size+1, 0), - ?line 0 = sizeof(Zero), - ?line ok = Module:pwrite(Fd, [{0, Data}, {Size+17, Data}]), - ?line {ok, [Data, - eof, - Data, - Zero]} = Module:pread(Fd, [{Size+17, Size+1}, - {2*Size+17+1, 1}, - {0, Size}, - {2*Size+17+1, 0}]), - ?line ok = Module:close(Fd), - %% - ?line ok. - -position(suite) -> - []; -position(doc) -> - ["Test that position/2 works."]; + io:format("~p:pread_pwrite_test(~p, ~p, ~p)~n", + [?MODULE, Module, Data, Options]), + %% + Size = sizeof(Data), + %% + {ok, Fd} = Module:open([], Options), + ok = Module:pwrite(Fd, 0, Data), + {ok, Data} = Module:pread(Fd, 0, Size+1), + eof = Module:pread(Fd, Size+1, 1), + {ok, Zero} = Module:pread(Fd, Size+1, 0), + 0 = sizeof(Zero), + ok = Module:pwrite(Fd, [{0, Data}, {Size+17, Data}]), + {ok, [Data, + eof, + Data, + Zero]} = Module:pread(Fd, [{Size+17, Size+1}, + {2*Size+17+1, 1}, + {0, Size}, + {2*Size+17+1, 0}]), + ok = Module:close(Fd), + %% + ok. + +%% Test that position/2 works. position(Config) when is_list(Config) -> - ?line Str = "Att vara eller icke vara, det är frågan. ", - ?line Bin = list_to_binary(Str), + Str = "Att vara eller icke vara, det är frågan. ", + Bin = list_to_binary(Str), %% position_test(?FILE_MODULE, Str, [ram, read]), position_test(?FILE_MODULE, Bin, [ram, binary]), @@ -217,79 +198,76 @@ position(Config) when is_list(Config) -> ok. position_test(Module, Data, Options) -> - ?line io:format("~p:position_test(~p, ~p, ~p)~n", - [?MODULE, Module, Data, Options]), - %% - ?line Size = sizeof(Data), - ?line Size_7 = Size+7, - %% - ?line Slice_0_2 = slice(Data, 0, 2), - ?line Slice_0_3 = slice(Data, 0, 3), - ?line Slice_2_5 = slice(Data, 2, 5), - ?line Slice_3_4 = slice(Data, 3, 4), - ?line Slice_5 = slice(Data, 5, Size), - %% - ?line {ok, Fd} = Module:open(Data, Options), - %% - ?line io:format("CUR positions"), - ?line {ok, Slice_0_2} = Module:read(Fd, 2), - ?line {ok, 2} = Module:position(Fd, cur), - ?line {ok, Slice_2_5} = Module:read(Fd, 5), - ?line {ok, 3} = Module:position(Fd, {cur, -4}), - ?line {ok, Slice_3_4} = Module:read(Fd, 4), - ?line {ok, 0} = Module:position(Fd, {cur, -7}), - ?line {ok, Slice_0_3} = Module:read(Fd, 3), - ?line {ok, 0} = Module:position(Fd, {cur, -3}), - ?line {error, einval} = Module:position(Fd, {cur, -1}), - ?line {ok, 0} = Module:position(Fd, 0), - ?line {ok, 2} = Module:position(Fd, {cur, 2}), - ?line {ok, Slice_2_5} = Module:read(Fd, 5), - ?line {ok, Size_7} = Module:position(Fd, {cur, Size}), - ?line {ok, Zero} = Module:read(Fd, 0), - ?line 0 = sizeof(Zero), - ?line eof = Module:read(Fd, 1), - %% - ?line io:format("Absolute and BOF positions"), - ?line {ok, Size} = Module:position(Fd, Size), - ?line eof = Module:read(Fd, 1), - ?line {ok, 5} = Module:position(Fd, 5), - ?line {ok, Slice_5} = Module:read(Fd, Size), - ?line {ok, 2} = Module:position(Fd, {bof, 2}), - ?line {ok, Slice_2_5} = Module:read(Fd, 5), - ?line {ok, 3} = Module:position(Fd, 3), - ?line {ok, Slice_3_4} = Module:read(Fd, 4), - ?line {ok, 0} = Module:position(Fd, bof), - ?line {ok, Slice_0_2} = Module:read(Fd, 2), - ?line {ok, Size_7} = Module:position(Fd, {bof, Size_7}), - ?line {ok, Zero} = Module:read(Fd, 0), - %% - ?line io:format("EOF positions"), - ?line {ok, Size} = Module:position(Fd, eof), - ?line eof = Module:read(Fd, 1), - ?line {ok, 5} = Module:position(Fd, {eof, -Size+5}), - ?line {ok, Slice_5} = Module:read(Fd, Size), - ?line {ok, 2} = Module:position(Fd, {eof, -Size+2}), - ?line {ok, Slice_2_5} = Module:read(Fd, 5), - ?line {ok, 3} = Module:position(Fd, {eof, -Size+3}), - ?line {ok, Slice_3_4} = Module:read(Fd, 4), - ?line {ok, 0} = Module:position(Fd, {eof, -Size}), - ?line {ok, Slice_0_2} = Module:read(Fd, 2), - ?line {ok, Size_7} = Module:position(Fd, {eof, 7}), - ?line {ok, Zero} = Module:read(Fd, 0), - ?line eof = Module:read(Fd, 1), - %% - ?line ok. - - - -truncate(suite) -> - []; -truncate(doc) -> - ["Test that truncate/1 works."]; + io:format("~p:position_test(~p, ~p, ~p)~n", + [?MODULE, Module, Data, Options]), + %% + Size = sizeof(Data), + Size_7 = Size+7, + %% + Slice_0_2 = slice(Data, 0, 2), + Slice_0_3 = slice(Data, 0, 3), + Slice_2_5 = slice(Data, 2, 5), + Slice_3_4 = slice(Data, 3, 4), + Slice_5 = slice(Data, 5, Size), + %% + {ok, Fd} = Module:open(Data, Options), + %% + io:format("CUR positions"), + {ok, Slice_0_2} = Module:read(Fd, 2), + {ok, 2} = Module:position(Fd, cur), + {ok, Slice_2_5} = Module:read(Fd, 5), + {ok, 3} = Module:position(Fd, {cur, -4}), + {ok, Slice_3_4} = Module:read(Fd, 4), + {ok, 0} = Module:position(Fd, {cur, -7}), + {ok, Slice_0_3} = Module:read(Fd, 3), + {ok, 0} = Module:position(Fd, {cur, -3}), + {error, einval} = Module:position(Fd, {cur, -1}), + {ok, 0} = Module:position(Fd, 0), + {ok, 2} = Module:position(Fd, {cur, 2}), + {ok, Slice_2_5} = Module:read(Fd, 5), + {ok, Size_7} = Module:position(Fd, {cur, Size}), + {ok, Zero} = Module:read(Fd, 0), + 0 = sizeof(Zero), + eof = Module:read(Fd, 1), + %% + io:format("Absolute and BOF positions"), + {ok, Size} = Module:position(Fd, Size), + eof = Module:read(Fd, 1), + {ok, 5} = Module:position(Fd, 5), + {ok, Slice_5} = Module:read(Fd, Size), + {ok, 2} = Module:position(Fd, {bof, 2}), + {ok, Slice_2_5} = Module:read(Fd, 5), + {ok, 3} = Module:position(Fd, 3), + {ok, Slice_3_4} = Module:read(Fd, 4), + {ok, 0} = Module:position(Fd, bof), + {ok, Slice_0_2} = Module:read(Fd, 2), + {ok, Size_7} = Module:position(Fd, {bof, Size_7}), + {ok, Zero} = Module:read(Fd, 0), + %% + io:format("EOF positions"), + {ok, Size} = Module:position(Fd, eof), + eof = Module:read(Fd, 1), + {ok, 5} = Module:position(Fd, {eof, -Size+5}), + {ok, Slice_5} = Module:read(Fd, Size), + {ok, 2} = Module:position(Fd, {eof, -Size+2}), + {ok, Slice_2_5} = Module:read(Fd, 5), + {ok, 3} = Module:position(Fd, {eof, -Size+3}), + {ok, Slice_3_4} = Module:read(Fd, 4), + {ok, 0} = Module:position(Fd, {eof, -Size}), + {ok, Slice_0_2} = Module:read(Fd, 2), + {ok, Size_7} = Module:position(Fd, {eof, 7}), + {ok, Zero} = Module:read(Fd, 0), + eof = Module:read(Fd, 1), + %% + ok. + + + +%% Test that truncate/1 works. truncate(Config) when is_list(Config) -> - ?line Str = "Mån ädlare att lida och fördraga " + Str = "Mån ädlare att lida och fördraga " ++ "ett bittert ödes stygn av pilar, ", - ?line Bin = list_to_binary(Str), + Bin = list_to_binary(Str), %% ok = truncate_test(?FILE_MODULE, Str, [ram, read, write]), ok = truncate_test(?FILE_MODULE, Bin, [ram, binary, read, write]), @@ -304,35 +282,32 @@ truncate(Config) when is_list(Config) -> ok. truncate_test(Module, Data, Options) -> - ?line io:format("~p:truncate_test(~p, ~p, ~p)~n", - [?MODULE, Module, Data, Options]), - %% - ?line Size = sizeof(Data), - ?line Size1 = Size-2, - ?line Data1 = slice(Data, 0, Size1), - %% - ?line {ok, Fd} = Module:open(Data, Options), - ?line {ok, Size1} = Module:position(Fd, Size1), - ?line case Module:truncate(Fd) of - ok -> - ?line {ok, 0} = Module:position(Fd, 0), - ?line {ok, Data1} = Module:read(Fd, Size), - ?line ok = Module:close(Fd), - ?line ok; - Error -> - ?line ok = Module:close(Fd), - ?line Error - end. - - - -sync(suite) -> - []; -sync(doc) -> - ["Test that sync/1 at least does not crash."]; + io:format("~p:truncate_test(~p, ~p, ~p)~n", + [?MODULE, Module, Data, Options]), + %% + Size = sizeof(Data), + Size1 = Size-2, + Data1 = slice(Data, 0, Size1), + %% + {ok, Fd} = Module:open(Data, Options), + {ok, Size1} = Module:position(Fd, Size1), + case Module:truncate(Fd) of + ok -> + {ok, 0} = Module:position(Fd, 0), + {ok, Data1} = Module:read(Fd, Size), + ok = Module:close(Fd), + ok; + Error -> + ok = Module:close(Fd), + Error + end. + + + +%% Test that sync/1 at least does not crash. sync(Config) when is_list(Config) -> - ?line Str = "än att ta till vapen mot ett hav av kval. ", - ?line Bin = list_to_binary(Str), + Str = "än att ta till vapen mot ett hav av kval. ", + Bin = list_to_binary(Str), %% sync_test(?FILE_MODULE, Str, [ram, read, write]), sync_test(?FILE_MODULE, Bin, [ram, binary, read, write]), @@ -347,28 +322,25 @@ sync(Config) when is_list(Config) -> ok. sync_test(Module, Data, Options) -> - ?line io:format("~p:sync_test(~p, ~p, ~p)~n", - [?MODULE, Module, Data, Options]), + io:format("~p:sync_test(~p, ~p, ~p)~n", + [?MODULE, Module, Data, Options]), %% - ?line Size = sizeof(Data), + Size = sizeof(Data), %% - ?line {ok, Fd} = Module:open(Data, Options), - ?line ok = Module:sync(Fd), - ?line {ok, Data} = Module:read(Fd, Size+1), - ?line ok. + {ok, Fd} = Module:open(Data, Options), + ok = Module:sync(Fd), + {ok, Data} = Module:read(Fd, Size+1), + ok. -get_set_file(suite) -> - []; -get_set_file(doc) -> - ["Tests get_file/1, set_file/2, get_file_close/1 and get_size/1."]; +%% Tests get_file/1, set_file/2, get_file_close/1 and get_size/1. get_set_file(Config) when is_list(Config) -> %% These two strings should not be of equal length. - ?line Str = "När högan nord blir snöbetäckt, ", - ?line Str2 = "får alla harar byta dräkt. ", - ?line Bin = list_to_binary(Str), - ?line Bin2 = list_to_binary(Str2), + Str = "När högan nord blir snöbetäckt, ", + Str2 = "får alla harar byta dräkt. ", + Bin = list_to_binary(Str), + Bin2 = list_to_binary(Str2), %% ok = get_set_file_test(Str, read_write, Str2), ok = get_set_file_test(Bin, [binary, read, write], Bin2), @@ -378,87 +350,84 @@ get_set_file(Config) when is_list(Config) -> ok. get_set_file_test(Data, Options, Data2) -> - ?line io:format("~p:get_set_file_test(~p, ~p, ~p)~n", - [?MODULE, Data, Options, Data2]), - %% - ?line Size = sizeof(Data), - ?line Size2 = sizeof(Data2), - %% - ?line {ok, Fd} = ?RAM_FILE_MODULE:open(Data, Options), - ?line {ok, Size} = ?RAM_FILE_MODULE:get_size(Fd), - ?line {ok, Data} = ?RAM_FILE_MODULE:get_file(Fd), - ?line {ok, Data} = ?RAM_FILE_MODULE:get_file_close(Fd), - ?line {error, einval} = ?RAM_FILE_MODULE:get_size(Fd), - ?line {ok, Fd2} = ?RAM_FILE_MODULE:open(Data, Options), - ?line case ?RAM_FILE_MODULE:set_file(Fd2, Data2) of - {ok, Size2} -> - ?line {ok, Size2} = ?RAM_FILE_MODULE:get_size(Fd2), - ?line {ok, Data2} = ?RAM_FILE_MODULE:get_file(Fd2), - ?line {ok, Data2} = ?RAM_FILE_MODULE:get_file_close(Fd2), - ?line ok; - {error, _} = Error -> - ?line {ok, Data} = ?RAM_FILE_MODULE:get_file_close(Fd2), - ?line Error - end. - - - -compress(suite) -> - []; -compress(doc) -> - ["Test that compress/1 and uncompress/1 works."]; + io:format("~p:get_set_file_test(~p, ~p, ~p)~n", + [?MODULE, Data, Options, Data2]), + %% + Size = sizeof(Data), + Size2 = sizeof(Data2), + %% + {ok, Fd} = ?RAM_FILE_MODULE:open(Data, Options), + {ok, Size} = ?RAM_FILE_MODULE:get_size(Fd), + {ok, Data} = ?RAM_FILE_MODULE:get_file(Fd), + {ok, Data} = ?RAM_FILE_MODULE:get_file_close(Fd), + {error, einval} = ?RAM_FILE_MODULE:get_size(Fd), + {ok, Fd2} = ?RAM_FILE_MODULE:open(Data, Options), + case ?RAM_FILE_MODULE:set_file(Fd2, Data2) of + {ok, Size2} -> + {ok, Size2} = ?RAM_FILE_MODULE:get_size(Fd2), + {ok, Data2} = ?RAM_FILE_MODULE:get_file(Fd2), + {ok, Data2} = ?RAM_FILE_MODULE:get_file_close(Fd2), + ok; + {error, _} = Error -> + {ok, Data} = ?RAM_FILE_MODULE:get_file_close(Fd2), + Error + end. + + + +%% Test that compress/1 and uncompress/1 works. compress(Config) when is_list(Config) -> - ?line Data = ?config(data_dir, Config), - ?line Real = filename:join(Data, "realmen.html"), - ?line RealGz = filename:join(Data, "realmen.html.gz"), + Data = proplists:get_value(data_dir, Config), + Real = filename:join(Data, "realmen.html"), + RealGz = filename:join(Data, "realmen.html.gz"), %% %% Uncompress test %% - ?line {ok, FdReal} = ?FILE_MODULE:open(Real, []), - ?line {ok, Fd} = ?FILE_MODULE:open([], [ram, read, write]), - ?line {ok, FdRealGz} = ?FILE_MODULE:open(RealGz, []), + {ok, FdReal} = ?FILE_MODULE:open(Real, []), + {ok, Fd} = ?FILE_MODULE:open([], [ram, read, write]), + {ok, FdRealGz} = ?FILE_MODULE:open(RealGz, []), %% - ?line {ok, SzGz} = ?FILE_MODULE:copy(FdRealGz, Fd), - ?line {ok, Sz} = ?RAM_FILE_MODULE:uncompress(Fd), - ?line {ok, 0} = ?FILE_MODULE:position(Fd, bof), - ?line true = compare(FdReal, Fd), + {ok, SzGz} = ?FILE_MODULE:copy(FdRealGz, Fd), + {ok, Sz} = ?RAM_FILE_MODULE:uncompress(Fd), + {ok, 0} = ?FILE_MODULE:position(Fd, bof), + true = compare(FdReal, Fd), %% - ?line true = (SzGz =< Sz), + true = (SzGz =< Sz), %% %% Compress and uncompress test %% - ?line {ok, 0} = ?FILE_MODULE:position(FdReal, bof), - ?line {ok, 0} = ?FILE_MODULE:position(Fd, bof), - ?line ok = ?FILE_MODULE:truncate(Fd), - ?line {ok, Sz} = ?FILE_MODULE:copy(FdReal, Fd), - ?line {ok, SzGz} = ?RAM_FILE_MODULE:compress(Fd), - ?line {ok, Sz} = ?RAM_FILE_MODULE:uncompress(Fd), - ?line {ok, 0} = ?FILE_MODULE:position(Fd, bof), - ?line {ok, 0} = ?FILE_MODULE:position(FdReal, bof), - ?line true = compare(FdReal, Fd), + {ok, 0} = ?FILE_MODULE:position(FdReal, bof), + {ok, 0} = ?FILE_MODULE:position(Fd, bof), + ok = ?FILE_MODULE:truncate(Fd), + {ok, Sz} = ?FILE_MODULE:copy(FdReal, Fd), + {ok, SzGz} = ?RAM_FILE_MODULE:compress(Fd), + {ok, Sz} = ?RAM_FILE_MODULE:uncompress(Fd), + {ok, 0} = ?FILE_MODULE:position(Fd, bof), + {ok, 0} = ?FILE_MODULE:position(FdReal, bof), + true = compare(FdReal, Fd), %% - ?line ok = ?FILE_MODULE:close(FdReal), - ?line ok = ?FILE_MODULE:close(Fd), - ?line ok = ?FILE_MODULE:close(FdRealGz), + ok = ?FILE_MODULE:close(FdReal), + ok = ?FILE_MODULE:close(Fd), + ok = ?FILE_MODULE:close(FdRealGz), %% Test uncompressing data that will be expanded many times. - ?line Huge = iolist_to_binary(mk_42(18)), - ?line HugeSize = byte_size(Huge), - ?line HugeGz = zlib:gzip(Huge), + Huge = iolist_to_binary(mk_42(18)), + HugeSize = byte_size(Huge), + HugeGz = zlib:gzip(Huge), - ?line {ok,HugeFd} = ?FILE_MODULE:open([], [ram,read,write,binary]), - ?line ok = ?FILE_MODULE:write(HugeFd, HugeGz), - ?line {ok,HugeSize} = ?RAM_FILE_MODULE:uncompress(HugeFd), - ?line {ok,0} = ?FILE_MODULE:position(HugeFd, bof), - ?line {ok,Huge} = ?FILE_MODULE:read(HugeFd, HugeSize), + {ok,HugeFd} = ?FILE_MODULE:open([], [ram,read,write,binary]), + ok = ?FILE_MODULE:write(HugeFd, HugeGz), + {ok,HugeSize} = ?RAM_FILE_MODULE:uncompress(HugeFd), + {ok,0} = ?FILE_MODULE:position(HugeFd, bof), + {ok,Huge} = ?FILE_MODULE:read(HugeFd, HugeSize), %% Uncompressing again should do nothing. - ?line {ok,HugeSize} = ?RAM_FILE_MODULE:uncompress(HugeFd), - ?line {ok,0} = ?FILE_MODULE:position(HugeFd, bof), - ?line {ok,Huge} = ?FILE_MODULE:read(HugeFd, HugeSize), + {ok,HugeSize} = ?RAM_FILE_MODULE:uncompress(HugeFd), + {ok,0} = ?FILE_MODULE:position(HugeFd, bof), + {ok,Huge} = ?FILE_MODULE:read(HugeFd, HugeSize), - ?line ok = ?FILE_MODULE:close(HugeFd), + ok = ?FILE_MODULE:close(HugeFd), ok. @@ -468,118 +437,108 @@ mk_42(N) -> B = mk_42(N-1), [B|B]. -uuencode(suite) -> - []; -uuencode(doc) -> - ["Test that uuencode/1 and uudecode/1 works."]; +%% Test that uuencode/1 and uudecode/1 works. uuencode(Config) when is_list(Config) -> - ?line Data = ?config(data_dir, Config), - ?line Real = filename:join(Data, "realmen.html"), - ?line RealUu = filename:join(Data, "realmen.html.uu"), + Data = proplists:get_value(data_dir, Config), + Real = filename:join(Data, "realmen.html"), + RealUu = filename:join(Data, "realmen.html.uu"), %% %% Uudecode test %% - ?line {ok, FdReal} = ?FILE_MODULE:open(Real, []), - ?line {ok, Fd} = ?FILE_MODULE:open([], [ram, read, write]), - ?line {ok, FdRealUu} = ?FILE_MODULE:open(RealUu, []), + {ok, FdReal} = ?FILE_MODULE:open(Real, []), + {ok, Fd} = ?FILE_MODULE:open([], [ram, read, write]), + {ok, FdRealUu} = ?FILE_MODULE:open(RealUu, []), %% - ?line {ok, SzUu} = ?FILE_MODULE:copy(FdRealUu, Fd), - ?line {ok, Sz} = ?RAM_FILE_MODULE:uudecode(Fd), - ?line true = (Sz =< SzUu), - ?line {ok, 0} = ?FILE_MODULE:position(Fd, bof), - ?line true = compare(FdReal, Fd), + {ok, SzUu} = ?FILE_MODULE:copy(FdRealUu, Fd), + {ok, Sz} = ?RAM_FILE_MODULE:uudecode(Fd), + true = (Sz =< SzUu), + {ok, 0} = ?FILE_MODULE:position(Fd, bof), + true = compare(FdReal, Fd), %% %% Uuencode and decode test %% F = fun(Offs) -> Size = Sz - Offs, - ?line {ok, Offs} = ?FILE_MODULE:position(FdReal, {bof,Offs}), - ?line {ok, 0} = ?FILE_MODULE:position(Fd, bof), - ?line ok = ?FILE_MODULE:truncate(Fd), - ?line {ok, Size} = ?FILE_MODULE:copy(FdReal, Fd), - ?line {ok, SizeUu} = ?RAM_FILE_MODULE:uuencode(Fd), - ?line true = (Size =< SizeUu), - ?line {ok, Size} = ?RAM_FILE_MODULE:uudecode(Fd), - ?line {ok, Offs} = ?FILE_MODULE:position(FdReal, {bof,Offs}), - ?line {ok, 0} = ?FILE_MODULE:position(Fd, bof), - ?line true = compare(FdReal, Fd) + {ok, Offs} = ?FILE_MODULE:position(FdReal, {bof,Offs}), + {ok, 0} = ?FILE_MODULE:position(Fd, bof), + ok = ?FILE_MODULE:truncate(Fd), + {ok, Size} = ?FILE_MODULE:copy(FdReal, Fd), + {ok, SizeUu} = ?RAM_FILE_MODULE:uuencode(Fd), + true = (Size =< SizeUu), + {ok, Size} = ?RAM_FILE_MODULE:uudecode(Fd), + {ok, Offs} = ?FILE_MODULE:position(FdReal, {bof,Offs}), + {ok, 0} = ?FILE_MODULE:position(Fd, bof), + true = compare(FdReal, Fd) end, lists:foreach(F, lists:seq(0,Sz-1, 43)), - ?line ok = ?FILE_MODULE:close(FdReal), - ?line ok = ?FILE_MODULE:close(Fd), - ?line ok = ?FILE_MODULE:close(FdRealUu), + ok = ?FILE_MODULE:close(FdReal), + ok = ?FILE_MODULE:close(Fd), + ok = ?FILE_MODULE:close(FdRealUu), %% ok. - -large_file_errors(suite) -> - []; -large_file_errors(doc) -> - ["Test error checking of large file offsets."]; +%% Test error checking of large file offsets. large_file_errors(Config) when is_list(Config) -> - ?line TwoGig = 1 bsl 31, - ?line {ok,Fd} = ?RAM_FILE_MODULE:open("1234567890", [read,write]), - ?line {error, einval} = ?FILE_MODULE:read(Fd, TwoGig), - ?line {error, badarg} = ?FILE_MODULE:read(Fd, -1), - ?line {error, einval} = ?FILE_MODULE:position(Fd, {bof,TwoGig}), - ?line {error, einval} = ?FILE_MODULE:position(Fd, {bof,-TwoGig-1}), - ?line {error, einval} = ?FILE_MODULE:position(Fd, {bof,-1}), - ?line {error, einval} = ?FILE_MODULE:position(Fd, {cur,TwoGig}), - ?line {error, einval} = ?FILE_MODULE:position(Fd, {cur,-TwoGig-1}), - ?line {error, einval} = ?FILE_MODULE:position(Fd, {eof,TwoGig}), - ?line {error, einval} = ?FILE_MODULE:position(Fd, {eof,-TwoGig-1}), - ?line {error, einval} = ?FILE_MODULE:pread(Fd, TwoGig, 1), - ?line {error, einval} = ?FILE_MODULE:pread(Fd, -TwoGig-1, 1), - ?line {error, einval} = ?FILE_MODULE:pread(Fd, -1, 1), - ?line {error, einval} = ?FILE_MODULE:pwrite(Fd, TwoGig, "@"), - ?line {error, einval} = ?FILE_MODULE:pwrite(Fd, -TwoGig-1, "@"), - ?line {error, einval} = ?FILE_MODULE:pwrite(Fd, -1, "@"), - ?line {error, einval} = ?FILE_MODULE:pread(Fd, TwoGig, 0), - ?line {error, einval} = ?FILE_MODULE:pread(Fd, -TwoGig-1, 0), - ?line {error, einval} = ?FILE_MODULE:pread(Fd, -1, 0), - ?line ok = ?FILE_MODULE:close(Fd), + TwoGig = 1 bsl 31, + {ok,Fd} = ?RAM_FILE_MODULE:open("1234567890", [read,write]), + {error, einval} = ?FILE_MODULE:read(Fd, TwoGig), + {error, badarg} = ?FILE_MODULE:read(Fd, -1), + {error, einval} = ?FILE_MODULE:position(Fd, {bof,TwoGig}), + {error, einval} = ?FILE_MODULE:position(Fd, {bof,-TwoGig-1}), + {error, einval} = ?FILE_MODULE:position(Fd, {bof,-1}), + {error, einval} = ?FILE_MODULE:position(Fd, {cur,TwoGig}), + {error, einval} = ?FILE_MODULE:position(Fd, {cur,-TwoGig-1}), + {error, einval} = ?FILE_MODULE:position(Fd, {eof,TwoGig}), + {error, einval} = ?FILE_MODULE:position(Fd, {eof,-TwoGig-1}), + {error, einval} = ?FILE_MODULE:pread(Fd, TwoGig, 1), + {error, einval} = ?FILE_MODULE:pread(Fd, -TwoGig-1, 1), + {error, einval} = ?FILE_MODULE:pread(Fd, -1, 1), + {error, einval} = ?FILE_MODULE:pwrite(Fd, TwoGig, "@"), + {error, einval} = ?FILE_MODULE:pwrite(Fd, -TwoGig-1, "@"), + {error, einval} = ?FILE_MODULE:pwrite(Fd, -1, "@"), + {error, einval} = ?FILE_MODULE:pread(Fd, TwoGig, 0), + {error, einval} = ?FILE_MODULE:pread(Fd, -TwoGig-1, 0), + {error, einval} = ?FILE_MODULE:pread(Fd, -1, 0), + ok = ?FILE_MODULE:close(Fd), ok. -large_file_light(suite) -> - []; -large_file_light(doc) -> - ["Test light operations on a \"large\" ram_file."]; +%% Test light operations on a \large\ ram_file. large_file_light(Config) when is_list(Config) -> - ?line PrivDir = ?config(priv_dir, Config), + PrivDir = proplists:get_value(priv_dir, Config), %% Marker for next test case that is to heavy to run in a suite. - ?line ok = ?FILE_MODULE:write_file( - filename:join(PrivDir, "large_file_light"), - <<"TAG">>), - %% - ?line Data = "abcdefghijklmnopqrstuvwzyz", - ?line Size = sizeof(Data), - ?line Max = (1 bsl 31) - 1, - ?line Max__1 = Max - 1, - ?line {ok, Fd} = ?RAM_FILE_MODULE:open(Data, [read]), - ?line {ok, Data} = ?FILE_MODULE:read(Fd, Size+1), - ?line {ok, Max__1} = ?FILE_MODULE:position(Fd, {eof, Max-Size-1}), - ?line eof = ?FILE_MODULE:read(Fd, 1), - ?line {ok, Max} = ?FILE_MODULE:position(Fd, {bof, Max}), - ?line {ok, Zero} = ?FILE_MODULE:read(Fd, 0), - ?line 0 = sizeof(Zero), - ?line eof = ?FILE_MODULE:read(Fd, 1), - ?line eof = ?FILE_MODULE:pread(Fd, Max__1, 1), - ?line {ok, Zero} = ?FILE_MODULE:pread(Fd, Max, 0), - ?line eof = ?FILE_MODULE:pread(Fd, Max, 1), + ok = ?FILE_MODULE:write_file( + filename:join(PrivDir, "large_file_light"), + <<"TAG">>), + %% + Data = "abcdefghijklmnopqrstuvwzyz", + Size = sizeof(Data), + Max = (1 bsl 31) - 1, + Max__1 = Max - 1, + {ok, Fd} = ?RAM_FILE_MODULE:open(Data, [read]), + {ok, Data} = ?FILE_MODULE:read(Fd, Size+1), + {ok, Max__1} = ?FILE_MODULE:position(Fd, {eof, Max-Size-1}), + eof = ?FILE_MODULE:read(Fd, 1), + {ok, Max} = ?FILE_MODULE:position(Fd, {bof, Max}), + {ok, Zero} = ?FILE_MODULE:read(Fd, 0), + 0 = sizeof(Zero), + eof = ?FILE_MODULE:read(Fd, 1), + eof = ?FILE_MODULE:pread(Fd, Max__1, 1), + {ok, Zero} = ?FILE_MODULE:pread(Fd, Max, 0), + eof = ?FILE_MODULE:pread(Fd, Max, 1), ok. -large_file_heavy(suite) -> - []; -large_file_heavy(doc) -> - ["Test operations on a maximum size (2 GByte - 1) ram_file."]; +large_file_heavy() -> + [{timetrap,{minutes,5}}]. + +%% Test operations on a maximum size (2 GByte - 1) ram_file. large_file_heavy(Config) when is_list(Config) -> - ?line PrivDir = ?config(priv_dir, Config), + PrivDir = proplists:get_value(priv_dir, Config), %% Check previous test case marker. case ?FILE_MODULE:read_file_info( filename:join(PrivDir, "large_file_light")) of @@ -590,33 +549,33 @@ large_file_heavy(Config) when is_list(Config) -> end. do_large_file_heavy(_Config) -> - ?line Data = "qwertyuiopasdfghjklzxcvbnm", - ?line Size = sizeof(Data), - ?line Max = (1 bsl 31) - 1, - ?line Max__1 = Max - 1, - ?line Max__3 = Max - 3, - ?line {ok, Fd} = ?RAM_FILE_MODULE:open(Data, [read,write]), - ?line {ok, Data} = ?FILE_MODULE:read(Fd, Size+1), - ?line {ok, Max} = ?FILE_MODULE:position(Fd, {eof, Max-Size}), - ?line eof = ?FILE_MODULE:read(Fd, 1), - ?line erlang:display({allocating,2,'GByte',please,be,patient,'...'}), - ?line ok = ?FILE_MODULE:write(Fd, ""), - ?line erlang:display({allocating,2,'GByte',succeeded}), - ?line {ok, Max__1} = ?FILE_MODULE:position(Fd, {eof, -1}), - ?line {ok, [0]} = ?FILE_MODULE:read(Fd, 1), - ?line {ok, []} = ?FILE_MODULE:read(Fd, 0), - ?line eof = ?FILE_MODULE:read(Fd, 1), - ?line ok = ?FILE_MODULE:pwrite(Fd, Max-3, "TAG"), - ?line {ok, Max} = ?FILE_MODULE:position(Fd, cur), - ?line {ok, Max__3} = ?FILE_MODULE:position(Fd, {eof, -3}), - ?line {ok, "TAG"} = ?FILE_MODULE:read(Fd, 3+1), - ?line {ok, Max__3} = ?FILE_MODULE:position(Fd, {cur, -3}), - ?line ok = ?FILE_MODULE:write(Fd, "tag"), - ?line {ok, Max} = ?FILE_MODULE:position(Fd, cur), - ?line {ok, 0} = ?FILE_MODULE:position(Fd, bof), - ?line {ok, "tag"} = ?FILE_MODULE:pread(Fd, Max__3, 3+1), - ?line {ok, 0} = ?FILE_MODULE:position(Fd, cur), - ?line ok = ?FILE_MODULE:close(Fd), + Data = "qwertyuiopasdfghjklzxcvbnm", + Size = sizeof(Data), + Max = (1 bsl 31) - 1, + Max__1 = Max - 1, + Max__3 = Max - 3, + {ok, Fd} = ?RAM_FILE_MODULE:open(Data, [read,write]), + {ok, Data} = ?FILE_MODULE:read(Fd, Size+1), + {ok, Max} = ?FILE_MODULE:position(Fd, {eof, Max-Size}), + eof = ?FILE_MODULE:read(Fd, 1), + erlang:display({allocating,2,'GByte',please,be,patient,'...'}), + ok = ?FILE_MODULE:write(Fd, ""), + erlang:display({allocating,2,'GByte',succeeded}), + {ok, Max__1} = ?FILE_MODULE:position(Fd, {eof, -1}), + {ok, [0]} = ?FILE_MODULE:read(Fd, 1), + {ok, []} = ?FILE_MODULE:read(Fd, 0), + eof = ?FILE_MODULE:read(Fd, 1), + ok = ?FILE_MODULE:pwrite(Fd, Max-3, "TAG"), + {ok, Max} = ?FILE_MODULE:position(Fd, cur), + {ok, Max__3} = ?FILE_MODULE:position(Fd, {eof, -3}), + {ok, "TAG"} = ?FILE_MODULE:read(Fd, 3+1), + {ok, Max__3} = ?FILE_MODULE:position(Fd, {cur, -3}), + ok = ?FILE_MODULE:write(Fd, "tag"), + {ok, Max} = ?FILE_MODULE:position(Fd, cur), + {ok, 0} = ?FILE_MODULE:position(Fd, bof), + {ok, "tag"} = ?FILE_MODULE:pread(Fd, Max__3, 3+1), + {ok, 0} = ?FILE_MODULE:position(Fd, cur), + ok = ?FILE_MODULE:close(Fd), ok. %%-------------------------------------------------------------------------- @@ -650,7 +609,7 @@ compare_data(A, B) when is_binary(A), is_list(B) -> A == list_to_binary(B); compare_data(A, B) when is_binary(A), is_binary(B) -> A == B. - + sizeof(Data) when is_list(Data) -> length(Data); sizeof(Data) when is_binary(Data) -> diff --git a/lib/kernel/test/rpc_SUITE.erl b/lib/kernel/test/rpc_SUITE.erl index 7adef49014..a89a7600a2 100644 --- a/lib/kernel/test/rpc_SUITE.erl +++ b/lib/kernel/test/rpc_SUITE.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2000-2011. All Rights Reserved. +%% Copyright Ericsson AB 2000-2017. 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/. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at %% -%% 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. +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% @@ -20,19 +21,22 @@ -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2]). --export([call/1, block_call/1, multicall/1, multicall_timeout/1, +-export([off_heap/1, + call/1, block_call/1, multicall/1, multicall_timeout/1, multicall_dies/1, multicall_node_dies/1, called_dies/1, called_node_dies/1, called_throws/1, call_benchmark/1, async_call/1]). -export([suicide/2, suicide/3, f/0, f2/0]). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap,{minutes,2}}]. all() -> - [call, block_call, multicall, multicall_timeout, + [off_heap, call, block_call, multicall, multicall_timeout, multicall_dies, multicall_node_dies, called_dies, called_node_dies, called_throws, call_benchmark, async_call]. @@ -52,274 +56,261 @@ init_per_group(_GroupName, Config) -> end_per_group(_GroupName, Config) -> Config. +off_heap(_Config) -> + %% The rex server process may receive a huge amount of + %% messages. Make sure that they are stored off heap to + %% avoid exessive GCs. + MQD = message_queue_data, + {MQD,off_heap} = process_info(whereis(rex), MQD), + ok. -call(doc) -> "Test different rpc calls"; +%% Test different rpc calls. call(Config) when is_list(Config) -> - Timetrap = ?t:timetrap(?t:seconds(30)), - ?line PA = filename:dirname(code:which(?MODULE)), + PA = filename:dirname(code:which(?MODULE)), %% Note. First part of nodename sets response delay in seconds - ?line {ok, N1} = ?t:start_node('3_rpc_SUITE_call', slave, - [{args, "-pa " ++ PA}]), - ?line {ok, N2} = ?t:start_node('1_rcp_SUITE_call', slave, - [{args, "-pa " ++ PA}]), - ?line {ok, N3} = ?t:start_node('4_rcp_SUITE_call', slave, - [{args, "-pa " ++ PA}]), - ?line {ok, N4} = ?t:start_node('8_rcp_SUITE_call', slave, - [{args, "-pa " ++ PA}]), - ?line ok = io:format("~p~n", [[N1, N2, N3]]), - ?line {hej,_,N1} = rpc:call(N1, ?MODULE, f, []), - ?line {hej,_,N2} = rpc:call(N2, ?MODULE, f, [], 2000), - ?line {badrpc,timeout} = rpc:call(N3, ?MODULE, f, [], 2000), - ?line receive after 6000 -> ok end, - ?line [] = flush([]), - ?line {hej,_,N4} = rpc:call(N4, ?MODULE, f, []), - ?line ?t:stop_node(N1), - ?line ?t:stop_node(N2), - ?line ?t:stop_node(N3), - ?line ?t:stop_node(N4), - ?t:timetrap_cancel(Timetrap), + {ok, N1} = test_server:start_node('3_rpc_SUITE_call', slave, + [{args, "-pa " ++ PA}]), + {ok, N2} = test_server:start_node('1_rcp_SUITE_call', slave, + [{args, "-pa " ++ PA}]), + {ok, N3} = test_server:start_node('4_rcp_SUITE_call', slave, + [{args, "-pa " ++ PA}]), + {ok, N4} = test_server:start_node('8_rcp_SUITE_call', slave, + [{args, "-pa " ++ PA}]), + ok = io:format("~p~n", [[N1, N2, N3]]), + {hej,_,N1} = rpc:call(N1, ?MODULE, f, []), + {hej,_,N2} = rpc:call(N2, ?MODULE, f, [], 2000), + {badrpc,timeout} = rpc:call(N3, ?MODULE, f, [], 2000), + receive after 6000 -> ok end, + [] = flush([]), + {hej,_,N4} = rpc:call(N4, ?MODULE, f, []), + test_server:stop_node(N1), + test_server:stop_node(N2), + test_server:stop_node(N3), + test_server:stop_node(N4), ok. -block_call(doc) -> "Test different rpc calls"; +%% Test different rpc calls. block_call(Config) when is_list(Config) -> - Timetrap = ?t:timetrap(?t:seconds(30)), - ?line PA = filename:dirname(code:which(?MODULE)), + PA = filename:dirname(code:which(?MODULE)), %% Note. First part of nodename sets response delay in seconds - ?line {ok, N1} = ?t:start_node('3_rpc_SUITE_block_call', slave, - [{args, "-pa " ++ PA}]), - ?line {ok, N2} = ?t:start_node('1_rcp_SUITE_block_call', slave, - [{args, "-pa " ++ PA}]), - ?line {ok, N3} = ?t:start_node('4_rcp_SUITE_block_call', slave, - [{args, "-pa " ++ PA}]), - ?line {ok, N4} = ?t:start_node('8_rcp_SUITE_block_call', slave, - [{args, "-pa " ++ PA}]), - ?line ok = io:format("~p~n", [[N1, N2, N3]]), - ?line {hej,_,N1} = rpc:block_call(N1, ?MODULE, f, []), - ?line {hej,_,N2} = rpc:block_call(N2, ?MODULE, f, [], 2000), - ?line {badrpc,timeout} = rpc:block_call(N3, ?MODULE, f, [], 2000), - ?line receive after 6000 -> ok end, - ?line [] = flush([]), - ?line {hej,_,N4} = rpc:block_call(N4, ?MODULE, f, []), - ?line ?t:stop_node(N1), - ?line ?t:stop_node(N2), - ?line ?t:stop_node(N3), - ?line ?t:stop_node(N4), - ?t:timetrap_cancel(Timetrap), + {ok, N1} = test_server:start_node('3_rpc_SUITE_block_call', slave, + [{args, "-pa " ++ PA}]), + {ok, N2} = test_server:start_node('1_rcp_SUITE_block_call', slave, + [{args, "-pa " ++ PA}]), + {ok, N3} = test_server:start_node('4_rcp_SUITE_block_call', slave, + [{args, "-pa " ++ PA}]), + {ok, N4} = test_server:start_node('8_rcp_SUITE_block_call', slave, + [{args, "-pa " ++ PA}]), + ok = io:format("~p~n", [[N1, N2, N3]]), + {hej,_,N1} = rpc:block_call(N1, ?MODULE, f, []), + {hej,_,N2} = rpc:block_call(N2, ?MODULE, f, [], 2000), + {badrpc,timeout} = rpc:block_call(N3, ?MODULE, f, [], 2000), + receive after 6000 -> ok end, + [] = flush([]), + {hej,_,N4} = rpc:block_call(N4, ?MODULE, f, []), + test_server:stop_node(N1), + test_server:stop_node(N2), + test_server:stop_node(N3), + test_server:stop_node(N4), ok. -multicall(doc) -> - "OTP-3449"; +%% OTP-3449. multicall(Config) when is_list(Config) -> - Timetrap = ?t:timetrap(?t:seconds(20)), - ?line PA = filename:dirname(code:which(?MODULE)), + PA = filename:dirname(code:which(?MODULE)), %% Note. First part of nodename sets response delay in seconds - ?line {ok, N1} = ?t:start_node('3_rpc_SUITE_multicall', slave, - [{args, "-pa " ++ PA}]), - ?line {ok, N2} = ?t:start_node('1_rcp_SUITE_multicall', slave, - [{args, "-pa " ++ PA}]), - ?line ok = io:format("~p~n", [[N1, N2]]), - ?line {[{hej,_,N1},{hej,_,N2}],[]} = - rpc:multicall([N1, N2], ?MODULE, f, []), - ?line Msgs = flush([]), - ?line [] = Msgs, - ?line ?t:stop_node(N1), - ?line ?t:stop_node(N2), - ?t:timetrap_cancel(Timetrap), + {ok, N1} = test_server:start_node('3_rpc_SUITE_multicall', slave, + [{args, "-pa " ++ PA}]), + {ok, N2} = test_server:start_node('1_rcp_SUITE_multicall', slave, + [{args, "-pa " ++ PA}]), + ok = io:format("~p~n", [[N1, N2]]), + {[{hej,_,N1},{hej,_,N2}],[]} = + rpc:multicall([N1, N2], ?MODULE, f, []), + Msgs = flush([]), + [] = Msgs, + test_server:stop_node(N1), + test_server:stop_node(N2), ok. multicall_timeout(Config) when is_list(Config) -> - Timetrap = ?t:timetrap(?t:seconds(30)), - ?line PA = filename:dirname(code:which(?MODULE)), + PA = filename:dirname(code:which(?MODULE)), %% Note. First part of nodename sets response delay in seconds - ?line {ok, N1} = ?t:start_node('11_rpc_SUITE_multicall', slave, - [{args, "-pa " ++ PA}]), - ?line {ok, N2} = ?t:start_node('8_rpc_SUITE_multicall', slave, - [{args, "-pa " ++ PA}]), - ?line {ok, N3} = ?t:start_node('5_rpc_SUITE_multicall', slave, - [{args, "-pa " ++ PA}]), - ?line {ok, N4} = ?t:start_node('2_rcp_SUITE_multicall', slave, - [{args, "-pa " ++ PA}]), - ?line ok = io:format("~p~n", [[N1, N2]]), - ?line {[{hej,_,N3},{hej,_,N4}],[N1, N2]} = - rpc:multicall([N3, N1, N2, N4], ?MODULE, f, [], ?t:seconds(6)), - ?t:sleep(?t:seconds(8)), %% Wait for late answers - ?line Msgs = flush([]), - ?line [] = Msgs, - ?line ?t:stop_node(N1), - ?line ?t:stop_node(N2), - ?line ?t:stop_node(N3), - ?line ?t:stop_node(N4), - ?t:timetrap_cancel(Timetrap), + {ok, N1} = test_server:start_node('11_rpc_SUITE_multicall', slave, + [{args, "-pa " ++ PA}]), + {ok, N2} = test_server:start_node('8_rpc_SUITE_multicall', slave, + [{args, "-pa " ++ PA}]), + {ok, N3} = test_server:start_node('5_rpc_SUITE_multicall', slave, + [{args, "-pa " ++ PA}]), + {ok, N4} = test_server:start_node('2_rcp_SUITE_multicall', slave, + [{args, "-pa " ++ PA}]), + ok = io:format("~p~n", [[N1, N2]]), + {[{hej,_,N3},{hej,_,N4}],[N1, N2]} = + rpc:multicall([N3, N1, N2, N4], ?MODULE, f, [], 6000), + ct:sleep({seconds,8}), %Wait for late answers + Msgs = flush([]), + [] = Msgs, + test_server:stop_node(N1), + test_server:stop_node(N2), + test_server:stop_node(N3), + test_server:stop_node(N4), ok. multicall_dies(Config) when is_list(Config) -> - Timetrap = ?t:timetrap(?t:seconds(30)), - ?line PA = filename:dirname(code:which(?MODULE)), - ?line {ok, N1} = ?t:start_node('rpc_SUITE_multicall_dies_1', slave, - [{args, "-pa " ++ PA}]), - ?line {ok, N2} = ?t:start_node('rcp_SUITE_multicall_dies_2', slave, - [{args, "-pa " ++ PA}]), - ?line Nodes = [N1, N2], + PA = filename:dirname(code:which(?MODULE)), + {ok, N1} = test_server:start_node('rpc_SUITE_multicall_dies_1', slave, + [{args, "-pa " ++ PA}]), + {ok, N2} = test_server:start_node('rcp_SUITE_multicall_dies_2', slave, + [{args, "-pa " ++ PA}]), + Nodes = [N1, N2], %% - ?line {[{badrpc, {'EXIT', normal}}, {badrpc, {'EXIT', normal}}], []} = + {[{badrpc, {'EXIT', normal}}, {badrpc, {'EXIT', normal}}], []} = do_multicall(Nodes, erlang, exit, [normal]), - ?line {[{badrpc, {'EXIT', abnormal}}, {badrpc, {'EXIT', abnormal}}], []} = + {[{badrpc, {'EXIT', abnormal}}, {badrpc, {'EXIT', abnormal}}], []} = do_multicall(Nodes, erlang, exit, [abnormal]), - ?line {[{badrpc, {'EXIT', {badarith, _}}}, - {badrpc, {'EXIT', {badarith, _}}}], - []} = + {[{badrpc, {'EXIT', {badarith, _}}}, + {badrpc, {'EXIT', {badarith, _}}}], + []} = do_multicall(Nodes, erlang, 'div', [1, 0]), - ?line {[{badrpc, {'EXIT', {badarg, _}}}, - {badrpc, {'EXIT', {badarg, _}}}], - []} = + {[{badrpc, {'EXIT', {badarg, _}}}, + {badrpc, {'EXIT', {badarg, _}}}], + []} = do_multicall(Nodes, erlang, atom_to_list, [1]), - ?line {[{badrpc, {'EXIT', {undef, _}}}, - {badrpc, {'EXIT', {undef, _}}}], - []} = + {[{badrpc, {'EXIT', {undef, _}}}, + {badrpc, {'EXIT', {undef, _}}}], + []} = do_multicall(Nodes, ?MODULE, suicide, []), - ?line {[timeout, timeout], []} = + {[timeout, timeout], []} = do_multicall(Nodes, ?MODULE, suicide, [link, normal]), - ?line {[{badrpc, {'EXIT', abnormal}}, {badrpc, {'EXIT', abnormal}}], []} = + {[{badrpc, {'EXIT', abnormal}}, {badrpc, {'EXIT', abnormal}}], []} = do_multicall(Nodes, ?MODULE, suicide, [link, abnormal]), - ?line {[timeout, timeout], []} = + {[timeout, timeout], []} = do_multicall(Nodes, ?MODULE, suicide, [exit, normal]), - ?line {[{badrpc, {'EXIT', abnormal}}, {badrpc, {'EXIT', abnormal}}], []} = + {[{badrpc, {'EXIT', abnormal}}, {badrpc, {'EXIT', abnormal}}], []} = do_multicall(Nodes, ?MODULE, suicide, [exit, abnormal]), - ?line {[{badrpc, {'EXIT', killed}}, {badrpc, {'EXIT', killed}}], []} = + {[{badrpc, {'EXIT', killed}}, {badrpc, {'EXIT', killed}}], []} = do_multicall(Nodes, ?MODULE, suicide, [exit, kill]), %% - ?line ?t:stop_node(N1), - ?line ?t:stop_node(N2), - ?t:timetrap_cancel(Timetrap), + test_server:stop_node(N1), + test_server:stop_node(N2), ok. do_multicall(Nodes, Mod, Func, Args) -> - ?line ok = io:format("~p:~p~p~n", [Mod, Func, Args]), - ?line Result = rpc:multicall(Nodes, Mod, Func, Args), - ?line Msgs = flush([]), - ?line [] = Msgs, + ok = io:format("~p:~p~p~n", [Mod, Func, Args]), + Result = rpc:multicall(Nodes, Mod, Func, Args), + Msgs = flush([]), + [] = Msgs, Result. -multicall_node_dies(doc) -> - ""; multicall_node_dies(Config) when is_list(Config) -> - Timetrap = ?t:timetrap(?t:seconds(60)), - %% do_multicall_2_nodes_dies(?MODULE, suicide, [erlang, halt, []]), do_multicall_2_nodes_dies(?MODULE, suicide, [init, stop, []]), do_multicall_2_nodes_dies(?MODULE, suicide, [rpc, stop, []]), - %% - ?t:timetrap_cancel(Timetrap), ok. do_multicall_2_nodes_dies(Mod, Func, Args) -> - ?line ok = io:format("~p:~p~p~n", [Mod, Func, Args]), - ?line PA = filename:dirname(code:which(?MODULE)), - ?line {ok, N1} = ?t:start_node('rpc_SUITE_multicall_node_dies_1', slave, - [{args, "-pa " ++ PA}]), - ?line {ok, N2} = ?t:start_node('rcp_SUITE_multicall_node_dies_2', slave, - [{args, "-pa " ++ PA}]), - ?line Nodes = [N1, N2], - ?line {[], Nodes} = rpc:multicall(Nodes, Mod, Func, Args), - ?line Msgs = flush([]), - ?line [] = Msgs, + ok = io:format("~p:~p~p~n", [Mod, Func, Args]), + PA = filename:dirname(code:which(?MODULE)), + {ok, N1} = test_server:start_node('rpc_SUITE_multicall_node_dies_1', slave, + [{args, "-pa " ++ PA}]), + {ok, N2} = test_server:start_node('rcp_SUITE_multicall_node_dies_2', slave, + [{args, "-pa " ++ PA}]), + Nodes = [N1, N2], + {[], Nodes} = rpc:multicall(Nodes, Mod, Func, Args), + Msgs = flush([]), + [] = Msgs, ok. -called_dies(doc) -> - "OTP-3766"; +%% OTP-3766. called_dies(Config) when is_list(Config) -> - Timetrap = ?t:timetrap(?t:seconds(210)), - ?line PA = filename:dirname(code:which(?MODULE)), - ?line {ok, N} = ?t:start_node(rpc_SUITE_called_dies, slave, - [{args, "-pa " ++ PA}]), + PA = filename:dirname(code:which(?MODULE)), + {ok, N} = test_server:start_node(rpc_SUITE_called_dies, slave, + [{args, "-pa " ++ PA}]), %% - ?line rep(fun (Tag, Call, Args) -> - {Tag,{badrpc,{'EXIT',normal}}} = - {Tag,apply(rpc, Call, Args)} - end, N, erlang, exit, [normal]), - ?line rep(fun (Tag, Call, Args) -> - {Tag,{badrpc,{'EXIT',abnormal}}} = - {Tag,apply(rpc, Call, Args)} - end, N, erlang, exit, [abnormal]), - ?line rep(fun (Tag, Call, Args) -> - {Tag,{badrpc,{'EXIT',{badarith,_}}}} = - {Tag,apply(rpc, Call, Args)} - end, N, erlang, 'div', [1,0]), - ?line rep(fun (Tag, Call, Args) -> - {Tag,{badrpc,{'EXIT',{badarg,_}}}} = - {Tag,apply(rpc, Call, Args)} - end, N, erlang, atom_to_list, [1]), - ?line rep(fun (Tag, Call, Args) -> - {Tag,{badrpc,{'EXIT',{undef,_}}}} = - {Tag,apply(rpc, Call, Args)} - end, N, ?MODULE, suicide, []), + rep(fun (Tag, Call, Args) -> + {Tag,{badrpc,{'EXIT',normal}}} = + {Tag,apply(rpc, Call, Args)} + end, N, erlang, exit, [normal]), + rep(fun (Tag, Call, Args) -> + {Tag,{badrpc,{'EXIT',abnormal}}} = + {Tag,apply(rpc, Call, Args)} + end, N, erlang, exit, [abnormal]), + rep(fun (Tag, Call, Args) -> + {Tag,{badrpc,{'EXIT',{badarith,_}}}} = + {Tag,apply(rpc, Call, Args)} + end, N, erlang, 'div', [1,0]), + rep(fun (Tag, Call, Args) -> + {Tag,{badrpc,{'EXIT',{badarg,_}}}} = + {Tag,apply(rpc, Call, Args)} + end, N, erlang, atom_to_list, [1]), + rep(fun (Tag, Call, Args) -> + {Tag,{badrpc,{'EXIT',{undef,_}}}} = + {Tag,apply(rpc, Call, Args)} + end, N, ?MODULE, suicide, []), %% TrapExit = process_flag(trap_exit, true), %% - ?line rep(fun (Tag, Call, Args=[Node|_]) when Node == node() -> - {Tag,timeout} = - {Tag,apply(rpc, Call, Args)}, - {Tag,flush,[{'EXIT',_,normal}]} = - {Tag,flush,flush([])}; - (Tag, Call, Args) -> - {Tag,timeout} = - {Tag,apply(rpc, Call, Args)} - end, N, ?MODULE, suicide, [link,normal]), - ?line rep(fun (Tag, Call, Args=[Node|_]) when Node == node() -> - {Tag,timeout} = - {Tag,apply(rpc, Call, Args)}, - {Tag,flush,[{'EXIT',_,abnormal}]} = - {Tag,flush,flush([])}; - (Tag, block_call, Args) -> - {Tag,timeout} = - {Tag,apply(rpc, block_call, Args)}; - (Tag, Call, Args) -> - {Tag,{badrpc,{'EXIT',abnormal}}} = - {Tag,apply(rpc, Call, Args)} - end, N, ?MODULE, suicide, [link,abnormal]), - ?line rep(fun (Tag, Call, Args=[Node|_]) when Node == node() -> - {Tag,timeout} = - {Tag,apply(rpc, Call, Args)}, - {Tag,flush,[{'EXIT',_,normal}]} = - {Tag,flush,flush([])}; - (Tag, Call, Args) -> - {Tag,timeout} = - {Tag,apply(rpc, Call, Args)} - end, N, ?MODULE, suicide, [exit,normal]), - ?line rep(fun (Tag, Call, Args=[Node|_]) when Node == node() -> - {Tag,timeout} = - {Tag,apply(rpc, Call, Args)}, - {Tag,flush,[{'EXIT',_,abnormal}]} = - {Tag,flush,flush([])}; - (Tag, block_call, Args) -> - {Tag,timeout} = - {Tag,apply(rpc, block_call, Args)}; - (Tag, Call, Args) -> - {Tag,{badrpc,{'EXIT',abnormal}}} = - {Tag,apply(rpc, Call, Args)} - end, N, ?MODULE, suicide, [exit,abnormal]), + rep(fun (Tag, Call, Args=[Node|_]) when Node == node() -> + {Tag,timeout} = + {Tag,apply(rpc, Call, Args)}, + {Tag,flush,[{'EXIT',_,normal}]} = + {Tag,flush,flush([])}; + (Tag, Call, Args) -> + {Tag,timeout} = + {Tag,apply(rpc, Call, Args)} + end, N, ?MODULE, suicide, [link,normal]), + rep(fun (Tag, Call, Args=[Node|_]) when Node == node() -> + {Tag,timeout} = + {Tag,apply(rpc, Call, Args)}, + {Tag,flush,[{'EXIT',_,abnormal}]} = + {Tag,flush,flush([])}; + (Tag, block_call, Args) -> + {Tag,timeout} = + {Tag,apply(rpc, block_call, Args)}; + (Tag, Call, Args) -> + {Tag,{badrpc,{'EXIT',abnormal}}} = + {Tag,apply(rpc, Call, Args)} + end, N, ?MODULE, suicide, [link,abnormal]), + rep(fun (Tag, Call, Args=[Node|_]) when Node == node() -> + {Tag,timeout} = + {Tag,apply(rpc, Call, Args)}, + {Tag,flush,[{'EXIT',_,normal}]} = + {Tag,flush,flush([])}; + (Tag, Call, Args) -> + {Tag,timeout} = + {Tag,apply(rpc, Call, Args)} + end, N, ?MODULE, suicide, [exit,normal]), + rep(fun (Tag, Call, Args=[Node|_]) when Node == node() -> + {Tag,timeout} = + {Tag,apply(rpc, Call, Args)}, + {Tag,flush,[{'EXIT',_,abnormal}]} = + {Tag,flush,flush([])}; + (Tag, block_call, Args) -> + {Tag,timeout} = + {Tag,apply(rpc, block_call, Args)}; + (Tag, Call, Args) -> + {Tag,{badrpc,{'EXIT',abnormal}}} = + {Tag,apply(rpc, Call, Args)} + end, N, ?MODULE, suicide, [exit,abnormal]), %% process_flag(trap_exit, TrapExit), %% - ?line rep(fun %% A local [exit,kill] would kill the test case process - (_Tag, _Call, [Node|_]) when Node == node() -> - ok; - %% A block_call [exit,kill] would kill the rpc server - (_Tag, block_call, _Args) -> ok; - (Tag, Call, Args) -> - {Tag,{badrpc,{'EXIT',killed}}} = - {Tag,apply(rpc, Call, Args)} - end, N, ?MODULE, suicide, [exit,kill]), + rep(fun %% A local [exit,kill] would kill the test case process + (_Tag, _Call, [Node|_]) when Node == node() -> + ok; + %% A block_call [exit,kill] would kill the rpc server + (_Tag, block_call, _Args) -> ok; + (Tag, Call, Args) -> + {Tag,{badrpc,{'EXIT',killed}}} = + {Tag,apply(rpc, Call, Args)} + end, N, ?MODULE, suicide, [exit,kill]), %% - ?line [] = flush([]), - ?line ?t:stop_node(N), - ?t:timetrap_cancel(Timetrap), + [] = flush([]), + test_server:stop_node(N), ok. rep(Fun, N, M, F, A) -> @@ -334,7 +325,7 @@ rep(Fun, N, M, F, A) -> Fun(9, block_call, [N, M, F, A, infinity]), Fun(10, block_call, [N, M, F, A, 3000]), ok. - + suicide(link, Reason) -> spawn_link( @@ -363,152 +354,124 @@ suicide(Mod, Func, Args) -> -called_node_dies(doc) -> - ""; -called_node_dies(suite) -> []; called_node_dies(Config) when is_list(Config) -> - Timetrap = ?t:timetrap(?t:minutes(2)), - ?line PA = filename:dirname(code:which(?MODULE)), - %% - ?line node_rep( - fun (Tag, Call, Args) -> - {Tag,{badrpc,nodedown}} = - {Tag,apply(rpc, Call, Args)} - end, "rpc_SUITE_called_node_dies_1", - PA, ?MODULE, suicide, [erlang,halt,[]]), - ?line node_rep( - fun (Tag, Call, Args) -> - {Tag,{badrpc,nodedown}} = - {Tag,apply(rpc, Call, Args)} - end, "rpc_SUITE_called_node_dies_2", - PA, ?MODULE, suicide, [init,stop,[]]), - ?line node_rep( - fun (Tag, Call, Args=[_|_]) -> - {Tag,{'EXIT',{killed,_}}} = - {Tag,catch {noexit,apply(rpc, Call, Args)}} - end, "rpc_SUITE_called_node_dies_3", - PA, ?MODULE, suicide, [erlang,exit,[rex,kill]]), - ?line node_rep( - fun %% Cannot block call rpc - will hang - (_Tag, block_call, _Args) -> ok; - (Tag, Call, Args=[_|_]) -> - {Tag,{'EXIT',{normal,_}}} = - {Tag,catch {noexit,apply(rpc, Call, Args)}} - end, "rpc_SUITE_called_node_dies_4", - PA, ?MODULE, suicide, [rpc,stop,[]]), - %% - ?t:timetrap_cancel(Timetrap), + PA = filename:dirname(code:which(?MODULE)), + + node_rep( + fun (Call, Args) -> + {badrpc,nodedown} = apply(rpc, Call, Args) + end, "rpc_SUITE_called_node_dies_1", + PA, ?MODULE, suicide, [erlang,halt,[]]), + + node_rep( + fun (Call, Args) -> + {badrpc,nodedown} = apply(rpc, Call, Args) + end, "rpc_SUITE_called_node_dies_2", + PA, ?MODULE, suicide, [init,stop,[]]), + + node_rep( + fun (Call, Args=[_|_]) -> + {badrpc,{'EXIT',{killed,_}}} = apply(rpc, Call, Args) + end, "rpc_SUITE_called_node_dies_3", + PA, ?MODULE, suicide, [erlang,exit,[rex,kill]]), + + node_rep( + fun (block_call, _Args) -> + %% Cannot block call rpc - will hang + ok; + (Call, Args=[_|_]) -> + {badrpc,{'EXIT',{normal,_}}} = apply(rpc, Call, Args) + end, "rpc_SUITE_called_node_dies_4", + PA, ?MODULE, suicide, [rpc,stop,[]]), + ok. node_rep(Fun, Name, PA, M, F, A) -> - {ok, Na} = ?t:start_node(list_to_atom(Name++"_a"), slave, - [{args, "-pa " ++ PA}]), - Fun(a, call, [Na, M, F, A]), - catch ?t:stop_node(Na), - {ok, Nb} = ?t:start_node(list_to_atom(Name++"_b"), slave, - [{args, "-pa " ++ PA}]), - Fun(b, call, [Nb, M, F, A, infinity]), - catch ?t:stop_node(Nb), - {ok, Nc} = ?t:start_node(list_to_atom(Name++"_c"), slave, - [{args, "-pa " ++ PA}]), - Fun(c, call, [Nc, M, F, A, infinity]), - catch ?t:stop_node(Nc), - %% - {ok, Nd} = ?t:start_node(list_to_atom(Name++"_d"), slave, - [{args, "-pa " ++ PA}]), - Fun(d, block_call, [Nd, M, F, A]), - catch ?t:stop_node(Nd), - {ok, Ne} = ?t:start_node(list_to_atom(Name++"_e"), slave, - [{args, "-pa " ++ PA}]), - Fun(e, block_call, [Ne, M, F, A, infinity]), - catch ?t:stop_node(Ne), - {ok, Nf} = ?t:start_node(list_to_atom(Name++"_f"), slave, - [{args, "-pa " ++ PA}]), - Fun(f, block_call, [Nf, M, F, A, infinity]), - catch ?t:stop_node(Nf), + node_rep_call(a, call, [M,F,A], Fun, Name, PA), + node_rep_call(b, call, [M,F,A,infinity], Fun, Name, PA), + node_rep_call(c, block_call, [M,F,A], Fun, Name, PA), + node_rep_call(d, block_call, [M,F,A,infinity], Fun, Name, PA). + +node_rep_call(Tag, Call, Args, Fun, Name0, PA) -> + Name = list_to_atom(Name0 ++ "_" ++ atom_to_list(Tag)), + {ok, N} = test_server:start_node(Name, slave, + [{args, "-pa " ++ PA}]), + Fun(Call, [N|Args]), + catch test_server:stop_node(N), ok. - - -called_throws(doc) -> - "OTP-3766"; +%% OTP-3766. called_throws(Config) when is_list(Config) -> - Timetrap = ?t:timetrap(?t:seconds(10)), - ?line PA = filename:dirname(code:which(?MODULE)), + PA = filename:dirname(code:which(?MODULE)), %% - ?line {ok, N} = ?t:start_node(rpc_SUITE_called_throws, slave, - [{args, "-pa " ++ PA}]), + {ok, N} = test_server:start_node(rpc_SUITE_called_throws, slave, + [{args, "-pa " ++ PA}]), %% - ?line rep(fun (Tag, Call, Args) -> - {Tag,up} = - {Tag,apply(rpc, Call, Args)} - end, N, erlang, throw, [up]), - ?line rep(fun (Tag, Call, Args) -> - {Tag,{badrpc,{'EXIT',reason}}} = - {Tag,apply(rpc, Call, Args)} - end, N, erlang, throw, [{'EXIT',reason}]), + rep(fun (Tag, Call, Args) -> + {Tag,up} = + {Tag,apply(rpc, Call, Args)} + end, N, erlang, throw, [up]), + rep(fun (Tag, Call, Args) -> + {Tag,{badrpc,{'EXIT',reason}}} = + {Tag,apply(rpc, Call, Args)} + end, N, erlang, throw, [{'EXIT',reason}]), %% - ?line ?t:stop_node(N), - ?t:timetrap_cancel(Timetrap), + test_server:stop_node(N), ok. call_benchmark(Config) when is_list(Config) -> - Timetrap = ?t:timetrap(?t:seconds(120)), - ?line PA = filename:dirname(code:which(?MODULE)), - ?line {ok, Node} = ?t:start_node(rpc_SUITE_call_benchmark, slave, - [{args, "-pa " ++ PA}]), + PA = filename:dirname(code:which(?MODULE)), + {ok, Node} = test_server:start_node(rpc_SUITE_call_benchmark, slave, + [{args, "-pa " ++ PA}]), Iter = case erlang:system_info(modified_timing_level) of undefined -> 10000; - _ -> 500 %Moified timing - spawn is slower + _ -> 500 %Modified timing - spawn is slower end, - ?line do_call_benchmark(Node, Iter), - ?t:timetrap_cancel(Timetrap), - ok. + Res = do_call_benchmark(Node, Iter), + test_server:stop_node(Node), + Res. do_call_benchmark(Node, M) when is_integer(M), M > 0 -> - do_call_benchmark(Node, erlang:now(), 0, M). - -do_call_benchmark(Node, {A,B,C}, M, M) -> - ?line {D,E,F} = erlang:now(), - ?line T = float(D-A)*1000000.0 + float(E-B) + float(F-C)*0.000001, - ?line Q = 3.0 * float(M) / T, - ?line ?t:stop_node(Node), - {comment, - lists:flatten([float_to_list(Q)," RPC calls per second"])}; -do_call_benchmark(Node, Then, I, M) -> - ?line Node = rpc:call(Node, erlang, node, []), - ?line _ = rpc:call(Node, erlang, whereis, [rex]), - ?line 3 = rpc:call(Node, erlang, '+', [1,2]), - ?line do_call_benchmark(Node, Then, I+1, M). + {Micros,ok} = timer:tc(fun() -> + do_call_benchmark(Node, 0, M) + end), + Calls = 3*M, + S = io_lib:format("~p RPC calls/second", [Calls*1000000 div Micros]), + {comment,lists:flatten(S)}. + +do_call_benchmark(_Node, M, M) -> + ok; +do_call_benchmark(Node, I, M) -> + Node = rpc:call(Node, erlang, node, []), + _ = rpc:call(Node, erlang, whereis, [rex]), + 3 = rpc:call(Node, erlang, '+', [1,2]), + do_call_benchmark(Node, I+1, M). async_call(Config) when is_list(Config) -> - Dog = ?t:timetrap(?t:seconds(120)), - %% Note: First part of nodename sets response delay in seconds. - ?line PA = filename:dirname(code:which(?MODULE)), - ?line NodeArgs = [{args,"-pa "++ PA}], - ?line {ok,Node1} = ?t:start_node('1_rpc_SUITE_call', slave, NodeArgs), - ?line {ok,Node2} = ?t:start_node('10_rpc_SUITE_call', slave, NodeArgs), - ?line {ok,Node3} = ?t:start_node('20_rpc_SUITE_call', slave, NodeArgs), - ?line Promise1 = rpc:async_call(Node1, ?MODULE, f, []), - ?line Promise2 = rpc:async_call(Node2, ?MODULE, f, []), - ?line Promise3 = rpc:async_call(Node3, ?MODULE, f, []), + PA = filename:dirname(code:which(?MODULE)), + NodeArgs = [{args,"-pa "++ PA}], + {ok,Node1} = test_server:start_node('1_rpc_SUITE_call', slave, NodeArgs), + {ok,Node2} = test_server:start_node('10_rpc_SUITE_call', slave, NodeArgs), + {ok,Node3} = test_server:start_node('20_rpc_SUITE_call', slave, NodeArgs), + Promise1 = rpc:async_call(Node1, ?MODULE, f, []), + Promise2 = rpc:async_call(Node2, ?MODULE, f, []), + Promise3 = rpc:async_call(Node3, ?MODULE, f, []), %% Test fast timeouts. - ?line timeout = rpc:nb_yield(Promise2), - ?line timeout = rpc:nb_yield(Promise2, 10), + timeout = rpc:nb_yield(Promise2), + timeout = rpc:nb_yield(Promise2, 10), %% Let Node1 finish its work before yielding. - ?t:sleep(?t:seconds(2)), - ?line {hej,_,Node1} = rpc:yield(Promise1), + ct:sleep({seconds,2}), + {hej,_,Node1} = rpc:yield(Promise1), %% Wait for the Node2 and Node3. - ?line {value,{hej,_,Node2}} = rpc:nb_yield(Promise2, infinity), - ?line {hej,_,Node3} = rpc:yield(Promise3), + {value,{hej,_,Node2}} = rpc:nb_yield(Promise2, infinity), + {hej,_,Node3} = rpc:yield(Promise3), - ?t:timetrap_cancel(Dog), ok. %%% diff --git a/lib/kernel/test/sendfile_SUITE.erl b/lib/kernel/test/sendfile_SUITE.erl index 123e849ccb..0c0b1cbcb6 100644 --- a/lib/kernel/test/sendfile_SUITE.erl +++ b/lib/kernel/test/sendfile_SUITE.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2011-2012. All Rights Reserved. +%% Copyright Ericsson AB 2011-2017. 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/. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at %% -%% 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. +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% @@ -22,37 +23,48 @@ -include_lib("common_test/include/ct.hrl"). -include_lib("kernel/include/file.hrl"). --compile(export_all). - -all() -> [{group,async_threads}, - {group,no_async_threads}]. - -groups() -> - [{async_threads,[],tcs()}, - {no_async_threads,[],tcs()}]. - -tcs() -> - [t_sendfile_small - ,t_sendfile_big_all - ,t_sendfile_big_size - ,t_sendfile_many_small - ,t_sendfile_partial - ,t_sendfile_offset - ,t_sendfile_sendafter - ,t_sendfile_recvafter - ,t_sendfile_recvafter_remoteclose - ,t_sendfile_sendduring - ,t_sendfile_recvduring - ,t_sendfile_closeduring - ,t_sendfile_crashduring - ]. +-export([all/0, init_per_suite/1, end_per_suite/1, init_per_testcase/2]). + +-export([sendfile_server/2, sendfile_do_recv/2, init/1, handle_event/2]). + +-export( + [t_sendfile_small/1, + t_sendfile_big_all/1, + t_sendfile_big_size/1, + t_sendfile_many_small/1, + t_sendfile_partial/1, + t_sendfile_offset/1, + t_sendfile_sendafter/1, + t_sendfile_recvafter/1, + t_sendfile_recvafter_remoteclose/1, + t_sendfile_sendduring/1, + t_sendfile_recvduring/1, + t_sendfile_closeduring/1, + t_sendfile_crashduring/1, + t_sendfile_arguments/1]). + +all() -> + [t_sendfile_small, + t_sendfile_big_all, + t_sendfile_big_size, + t_sendfile_many_small, + t_sendfile_partial, + t_sendfile_offset, + t_sendfile_sendafter, + t_sendfile_recvafter, + t_sendfile_recvafter_remoteclose, + t_sendfile_sendduring, + t_sendfile_recvduring, + t_sendfile_closeduring, + t_sendfile_crashduring, + t_sendfile_arguments]. init_per_suite(Config) -> case {os:type(),os:version()} of {{unix,sunos}, {5,8,_}} -> {skip, "Solaris 8 not supported for now"}; _ -> - Priv = ?config(priv_dir, Config), + Priv = proplists:get_value(priv_dir, Config), SFilename = filename:join(Priv, "sendfile_small.html"), {ok, DS} = file:open(SFilename,[write,raw]), file:write(DS,"yo baby yo"), @@ -71,43 +83,32 @@ init_per_suite(Config) -> end_per_suite(Config) -> file:delete(proplists:get_value(big_file, Config)). -init_per_group(async_threads,Config) -> - case erlang:system_info(thread_pool_size) of - 0 -> - {skip,"No async threads"}; - _ -> - [{sendfile_opts,[{use_threads,true}]}|Config] - end; -init_per_group(no_async_threads,Config) -> - [{sendfile_opts,[{use_threads,false}]}|Config]. - -end_per_group(_,_Config) -> - ok. - init_per_testcase(TC,Config) when TC == t_sendfile_recvduring; TC == t_sendfile_sendduring -> Filename = proplists:get_value(small_file, Config), Send = fun(Sock) -> {_Size, Data} = sendfile_file_info(Filename), - {ok,D} = file:open(Filename, [raw,binary,read]), - prim_file:sendfile(D, Sock, 0, 0, 0, - [],[],[]), + {ok,Fd} = file:open(Filename, [raw,binary,read]), + %% Determine whether the driver has native support by + %% hitting the raw module directly; file:sendfile/5 will + %% land in the fallback if it doesn't. + RawModule = Fd#file_descriptor.module, + {ok, _Ignored} = RawModule:sendfile(Fd,Sock,0,0,0,[],[],[]), Data end, %% Check if sendfile is supported on this platform case catch sendfile_send(Send) of ok -> - Config; + init_per_testcase(t_sendfile, Config); Error -> ct:log("Error: ~p",[Error]), {skip,"Not supported"} end; -init_per_testcase(_Tc,Config) -> +init_per_testcase(_TC,Config) -> Config. - t_sendfile_small(Config) when is_list(Config) -> Filename = proplists:get_value(small_file, Config), @@ -123,7 +124,7 @@ t_sendfile_small(Config) when is_list(Config) -> t_sendfile_many_small(Config) when is_list(Config) -> Filename = proplists:get_value(small_file, Config), FileOpts = proplists:get_value(file_opts, Config, []), - SendfileOpts = proplists:get_value(sendfile_opts, Config), + SendfileOpts = proplists:get_value(sendfile_opts, Config, []), error_logger:add_report_handler(?MODULE,[self()]), @@ -150,7 +151,7 @@ t_sendfile_many_small(Config) when is_list(Config) -> t_sendfile_big_all(Config) when is_list(Config) -> Filename = proplists:get_value(big_file, Config), - SendfileOpts = proplists:get_value(sendfile_opts, Config), + SendfileOpts = proplists:get_value(sendfile_opts, Config, []), Send = fun(Sock) -> {ok, #file_info{size = Size}} = @@ -164,7 +165,7 @@ t_sendfile_big_all(Config) when is_list(Config) -> t_sendfile_big_size(Config) -> Filename = proplists:get_value(big_file, Config), FileOpts = proplists:get_value(file_opts, Config, []), - SendfileOpts = proplists:get_value(sendfile_opts, Config), + SendfileOpts = proplists:get_value(sendfile_opts, Config, []), SendAll = fun(Sock) -> {ok, #file_info{size = Size}} = @@ -179,7 +180,7 @@ t_sendfile_big_size(Config) -> t_sendfile_partial(Config) -> Filename = proplists:get_value(small_file, Config), FileOpts = proplists:get_value(file_opts, Config, []), - SendfileOpts = proplists:get_value(sendfile_opts, Config), + SendfileOpts = proplists:get_value(sendfile_opts, Config, []), SendSingle = fun(Sock) -> {_Size, <<Data:5/binary,_/binary>>} = @@ -216,7 +217,7 @@ t_sendfile_partial(Config) -> t_sendfile_offset(Config) -> Filename = proplists:get_value(small_file, Config), FileOpts = proplists:get_value(file_opts, Config, []), - SendfileOpts = proplists:get_value(sendfile_opts, Config), + SendfileOpts = proplists:get_value(sendfile_opts, Config, []), Send = fun(Sock) -> {_Size, <<_:5/binary,Data:3/binary,_/binary>> = AllData} = @@ -232,7 +233,7 @@ t_sendfile_offset(Config) -> t_sendfile_sendafter(Config) -> Filename = proplists:get_value(small_file, Config), - SendfileOpts = proplists:get_value(sendfile_opts, Config), + SendfileOpts = proplists:get_value(sendfile_opts, Config, []), Send = fun(Sock) -> {Size, Data} = sendfile_file_info(Filename), @@ -245,7 +246,7 @@ t_sendfile_sendafter(Config) -> t_sendfile_recvafter(Config) -> Filename = proplists:get_value(small_file, Config), - SendfileOpts = proplists:get_value(sendfile_opts, Config), + SendfileOpts = proplists:get_value(sendfile_opts, Config, []), Send = fun(Sock) -> {Size, Data} = sendfile_file_info(Filename), @@ -278,7 +279,7 @@ t_sendfile_recvafter_remoteclose(Config) -> t_sendfile_sendduring(Config) -> Filename = proplists:get_value(big_file, Config), - SendfileOpts = proplists:get_value(sendfile_opts, Config), + SendfileOpts = proplists:get_value(sendfile_opts, Config, []), Send = fun(Sock) -> {ok, #file_info{size = Size}} = @@ -295,7 +296,7 @@ t_sendfile_sendduring(Config) -> t_sendfile_recvduring(Config) -> Filename = proplists:get_value(big_file, Config), - SendfileOpts = proplists:get_value(sendfile_opts, Config), + SendfileOpts = proplists:get_value(sendfile_opts, Config, []), Send = fun(Sock) -> {ok, #file_info{size = Size}} = @@ -314,7 +315,7 @@ t_sendfile_recvduring(Config) -> t_sendfile_closeduring(Config) -> Filename = proplists:get_value(big_file, Config), - SendfileOpts = proplists:get_value(sendfile_opts, Config), + SendfileOpts = proplists:get_value(sendfile_opts, Config, []), Send = fun(Sock,SFServPid) -> spawn_link(fun() -> @@ -344,7 +345,7 @@ t_sendfile_closeduring(Config) -> t_sendfile_crashduring(Config) -> Filename = proplists:get_value(big_file, Config), - SendfileOpts = proplists:get_value(sendfile_opts, Config), + SendfileOpts = proplists:get_value(sendfile_opts, Config, []), error_logger:add_report_handler(?MODULE,[self()]), @@ -372,6 +373,36 @@ t_sendfile_crashduring(Config) -> end end. +t_sendfile_arguments(Config) -> + Filename = proplists:get_value(small_file, Config), + + {ok, Listener} = gen_tcp:listen(0, + [{packet, 0}, {active, false}, {reuseaddr, true}]), + {ok, Port} = inet:port(Listener), + + ErrorCheck = + fun(Reason, Offset, Length, Opts) -> + {ok, Sender} = gen_tcp:connect({127, 0, 0, 1}, Port, + [{packet, 0}, {active, false}]), + {ok, Receiver} = gen_tcp:accept(Listener), + {ok, Fd} = file:open(Filename, [read, raw]), + {error, Reason} = file:sendfile(Fd, Sender, Offset, Length, Opts), + gen_tcp:close(Receiver), + gen_tcp:close(Sender), + file:close(Fd) + end, + + ErrorCheck(einval, -1, 0, []), + ErrorCheck(einval, 0, -1, []), + ErrorCheck(badarg, gurka, 0, []), + ErrorCheck(badarg, 0, gurka, []), + ErrorCheck(badarg, 0, 0, gurka), + ErrorCheck(badarg, 0, 0, [{chunk_size, gurka}]), + + gen_tcp:close(Listener), + + ok. + %% Generic sendfile server code sendfile_send(Send) -> sendfile_send({127,0,0,1},Send). diff --git a/lib/kernel/test/seq_trace_SUITE.erl b/lib/kernel/test/seq_trace_SUITE.erl index 47eeb4df4c..cf4bf11328 100644 --- a/lib/kernel/test/seq_trace_SUITE.erl +++ b/lib/kernel/test/seq_trace_SUITE.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2011. All Rights Reserved. +%% Copyright Ericsson AB 1998-2018. 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/. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at %% -%% 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. +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% @@ -24,25 +25,29 @@ -export([token_set_get/1, tracer_set_get/1, print/1, send/1, distributed_send/1, recv/1, distributed_recv/1, trace_exit/1, distributed_exit/1, call/1, port/1, - match_set_seq_token/1, gc_seq_token/1]). + match_set_seq_token/1, gc_seq_token/1, label_capability_mismatch/1]). -% internal exports +%% internal exports -export([simple_tracer/2, one_time_receiver/0, one_time_receiver/1, start_tracer/0, stop_tracer/1, do_match_set_seq_token/1, do_gc_seq_token/1, countdown_start/2]). - %-define(line_trace, 1). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). --define(default_timeout, ?t:minutes(1)). +-define(TIMESTAMP_MODES, [no_timestamp, + timestamp, + monotonic_timestamp, + strict_monotonic_timestamp]). -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap,{minutes,1}}]. all() -> [token_set_get, tracer_set_get, print, send, distributed_send, recv, distributed_recv, trace_exit, distributed_exit, call, port, match_set_seq_token, - gc_seq_token]. + gc_seq_token, label_capability_mismatch]. groups() -> []. @@ -61,343 +66,458 @@ end_per_group(_GroupName, Config) -> init_per_testcase(_Case, Config) -> - ?line Dog = test_server:timetrap(?default_timeout), - [{watchdog, Dog}|Config]. + Config. -end_per_testcase(_Case, Config) -> - Dog=?config(watchdog, Config), - test_server:timetrap_cancel(Dog), +end_per_testcase(_Case, _Config) -> ok. %% Verifies that the set_token and get_token functions work as expected -token_set_get(doc) -> []; -token_set_get(suite) -> []; token_set_get(Config) when is_list(Config) -> - ?line Self = self(), - ?line seq_trace:reset_trace(), + do_token_set_get(timestamp), + do_token_set_get(monotonic_timestamp), + do_token_set_get(strict_monotonic_timestamp). + +do_token_set_get(TsType) -> + io:format("Testing ~p~n", [TsType]), + Flags = case TsType of + timestamp -> 15; + strict_monotonic_timestamp -> 23; + monotonic_timestamp -> 39 + end, + Self = self(), + seq_trace:reset_trace(), %% Test that initial seq_trace is disabled - ?line [] = seq_trace:get_token(), + [] = seq_trace:get_token(), %% Test setting and reading the different fields - ?line 0 = seq_trace:set_token(label,17), - ?line {label,17} = seq_trace:get_token(label), - ?line false = seq_trace:set_token(print,true), - ?line {print,true} = seq_trace:get_token(print), - ?line false = seq_trace:set_token(send,true), - ?line {send,true} = seq_trace:get_token(send), - ?line false = seq_trace:set_token('receive',true), - ?line {'receive',true} = seq_trace:get_token('receive'), - ?line false = seq_trace:set_token(timestamp,true), - ?line {timestamp,true} = seq_trace:get_token(timestamp), + 0 = seq_trace:set_token(label,{my_label,1}), + {label,{my_label,1}} = seq_trace:get_token(label), + false = seq_trace:set_token(print,true), + {print,true} = seq_trace:get_token(print), + false = seq_trace:set_token(send,true), + {send,true} = seq_trace:get_token(send), + false = seq_trace:set_token('receive',true), + {'receive',true} = seq_trace:get_token('receive'), + false = seq_trace:set_token(TsType,true), + {TsType,true} = seq_trace:get_token(TsType), %% Check the whole token - ?line {15,17,0,Self,0} = seq_trace:get_token(), % all flags are set + {Flags,{my_label,1},0,Self,0} = seq_trace:get_token(), % all flags are set %% Test setting and reading the 'serial' field - ?line {0,0} = seq_trace:set_token(serial,{3,5}), - ?line {serial,{3,5}} = seq_trace:get_token(serial), + {0,0} = seq_trace:set_token(serial,{3,5}), + {serial,{3,5}} = seq_trace:get_token(serial), %% Check the whole token, test that a whole token can be set and get - ?line {15,17,5,Self,3} = seq_trace:get_token(), - ?line seq_trace:set_token({15,19,7,Self,5}), - ?line {15,19,7,Self,5} = seq_trace:get_token(), + {Flags,{my_label,1},5,Self,3} = seq_trace:get_token(), + seq_trace:set_token({Flags,19,7,Self,5}), + {Flags,19,7,Self,5} = seq_trace:get_token(), %% Check that receive timeout does not reset token - ?line receive after 0 -> ok end, - ?line {15,19,7,Self,5} = seq_trace:get_token(), + receive after 0 -> ok end, + {Flags,19,7,Self,5} = seq_trace:get_token(), %% Check that token can be unset - ?line {15,19,7,Self,5} = seq_trace:set_token([]), - ?line [] = seq_trace:get_token(), + {Flags,19,7,Self,5} = seq_trace:set_token([]), + [] = seq_trace:get_token(), %% Check that Previous serial counter survived unset token - ?line 0 = seq_trace:set_token(label, 17), - ?line {0,17,0,Self,5} = seq_trace:get_token(), + 0 = seq_trace:set_token(label, 17), + {0,17,0,Self,5} = seq_trace:get_token(), %% Check that reset_trace resets the token and clears %% the Previous serial counter - ?line seq_trace:reset_trace(), - ?line [] = seq_trace:get_token(), - ?line 0 = seq_trace:set_token(label, 19), - ?line {0,19,0,Self,0} = seq_trace:get_token(), + seq_trace:reset_trace(), + [] = seq_trace:get_token(), + 0 = seq_trace:set_token(label, 19), + {0,19,0,Self,0} = seq_trace:get_token(), %% Cleanup - ?line seq_trace:reset_trace(), + seq_trace:reset_trace(), ok. -tracer_set_get(doc) -> []; -tracer_set_get(suite) -> []; tracer_set_get(Config) when is_list(Config) -> - ?line Self = self(), - ?line seq_trace:set_system_tracer(self()), - ?line Self = seq_trace:get_system_tracer(), - ?line Self = seq_trace:set_system_tracer(false), - ?line false = seq_trace:get_system_tracer(), + Self = self(), + seq_trace:set_system_tracer(self()), + Self = seq_trace:get_system_tracer(), + Self = seq_trace:set_system_tracer(false), + false = seq_trace:get_system_tracer(), %% Set the system tracer to a port. - ?line Port = load_tracer(Config), - ?line seq_trace:set_system_tracer(Port), - ?line Port = seq_trace:get_system_tracer(), - ?line Port = seq_trace:set_system_tracer(false), - ?line false = seq_trace:get_system_tracer(), + Port = load_tracer(Config), + seq_trace:set_system_tracer(Port), + Port = seq_trace:get_system_tracer(), + Port = seq_trace:set_system_tracer(false), + false = seq_trace:get_system_tracer(), ok. -print(doc) -> []; -print(suite) -> []; print(Config) when is_list(Config) -> - ?line start_tracer(), - ?line seq_trace:set_token(print,true), - ?line seq_trace:print(0,print1), - ?line seq_trace:print(1,print2), - ?line seq_trace:print(print3), - ?line seq_trace:reset_trace(), - ?line [{0,{print,_,_,[],print1}}, - {0,{print,_,_,[],print3}}] = stop_tracer(2). + lists:foreach(fun do_print/1, ?TIMESTAMP_MODES). + +do_print(TsType) -> + start_tracer(), + set_token_flags([print, TsType]), + seq_trace:print(0,print1), + seq_trace:print(1,print2), + seq_trace:print(print3), + seq_trace:reset_trace(), + [{0,{print,_,_,[],print1}, Ts0}, + {0,{print,_,_,[],print3}, Ts1}] = stop_tracer(2), + check_ts(TsType, Ts0), + check_ts(TsType, Ts1). -send(doc) -> []; -send(suite) -> []; send(Config) when is_list(Config) -> - ?line seq_trace:reset_trace(), - ?line start_tracer(), - ?line Receiver = spawn(?MODULE,one_time_receiver,[]), - ?line seq_trace:set_token(send,true), - ?line Receiver ! send, - ?line Self = self(), - ?line seq_trace:reset_trace(), - ?line [{0,{send,_,Self,Receiver,send}}] = stop_tracer(1). - -distributed_send(doc) -> []; -distributed_send(suite) -> []; + lists:foreach(fun do_send/1, ?TIMESTAMP_MODES). + +do_send(TsType) -> + seq_trace:reset_trace(), + start_tracer(), + Receiver = spawn(?MODULE,one_time_receiver,[]), + Label = make_ref(), + seq_trace:set_token(label,Label), + set_token_flags([send, TsType]), + Receiver ! send, + Self = self(), + seq_trace:reset_trace(), + [{Label,{send,_,Self,Receiver,send}, Ts}] = stop_tracer(1), + check_ts(TsType, Ts). + distributed_send(Config) when is_list(Config) -> - ?line {ok,Node} = start_node(seq_trace_other,[]), - ?line {_,Dir} = code:is_loaded(?MODULE), - ?line Mdir = filename:dirname(Dir), - ?line true = rpc:call(Node,code,add_patha,[Mdir]), - ?line seq_trace:reset_trace(), - ?line start_tracer(), - ?line Receiver = spawn(Node,?MODULE,one_time_receiver,[]), - ?line seq_trace:set_token(send,true), - ?line Receiver ! send, - ?line Self = self(), - ?line seq_trace:reset_trace(), - ?line stop_node(Node), - ?line [{0,{send,_,Self,Receiver,send}}] = stop_tracer(1). - -recv(doc) -> []; -recv(suite) -> []; + lists:foreach(fun do_distributed_send/1, ?TIMESTAMP_MODES). + +do_distributed_send(TsType) -> + {ok,Node} = start_node(seq_trace_other,[]), + {_,Dir} = code:is_loaded(?MODULE), + Mdir = filename:dirname(Dir), + true = rpc:call(Node,code,add_patha,[Mdir]), + seq_trace:reset_trace(), + start_tracer(), + Receiver = spawn(Node,?MODULE,one_time_receiver,[]), + + %% Make sure complex labels survive the trip. + Label = make_ref(), + seq_trace:set_token(label,Label), + set_token_flags([send,TsType]), + + Receiver ! send, + Self = self(), + seq_trace:reset_trace(), + stop_node(Node), + [{Label,{send,_,Self,Receiver,send}, Ts}] = stop_tracer(1), + check_ts(TsType, Ts). + + recv(Config) when is_list(Config) -> - ?line seq_trace:reset_trace(), - ?line start_tracer(), - ?line Receiver = spawn(?MODULE,one_time_receiver,[]), - ?line seq_trace:set_token('receive',true), - ?line Receiver ! 'receive', + lists:foreach(fun do_recv/1, ?TIMESTAMP_MODES). + +do_recv(TsType) -> + seq_trace:reset_trace(), + start_tracer(), + Receiver = spawn(?MODULE,one_time_receiver,[]), + set_token_flags(['receive',TsType]), + Receiver ! 'receive', %% let the other process receive the message: - ?line receive after 1 -> ok end, - ?line Self = self(), - ?line seq_trace:reset_trace(), - ?line [{0,{'receive',_,Self,Receiver,'receive'}}] = stop_tracer(1). + receive after 1 -> ok end, + Self = self(), + seq_trace:reset_trace(), + [{0,{'receive',_,Self,Receiver,'receive'}, Ts}] = stop_tracer(1), + check_ts(TsType, Ts). -distributed_recv(doc) -> []; -distributed_recv(suite) -> []; distributed_recv(Config) when is_list(Config) -> - ?line {ok,Node} = start_node(seq_trace_other,[]), - ?line {_,Dir} = code:is_loaded(?MODULE), - ?line Mdir = filename:dirname(Dir), - ?line true = rpc:call(Node,code,add_patha,[Mdir]), - ?line seq_trace:reset_trace(), - ?line rpc:call(Node,?MODULE,start_tracer,[]), - ?line Receiver = spawn(Node,?MODULE,one_time_receiver,[]), - ?line seq_trace:set_token('receive',true), - ?line Receiver ! 'receive', + lists:foreach(fun do_distributed_recv/1, ?TIMESTAMP_MODES). + +do_distributed_recv(TsType) -> + {ok,Node} = start_node(seq_trace_other,[]), + {_,Dir} = code:is_loaded(?MODULE), + Mdir = filename:dirname(Dir), + true = rpc:call(Node,code,add_patha,[Mdir]), + seq_trace:reset_trace(), + rpc:call(Node,?MODULE,start_tracer,[]), + Receiver = spawn(Node,?MODULE,one_time_receiver,[]), + + %% Make sure complex labels survive the trip. + Label = make_ref(), + seq_trace:set_token(label,Label), + set_token_flags(['receive',TsType]), + + Receiver ! 'receive', %% let the other process receive the message: - ?line receive after 1 -> ok end, - ?line Self = self(), - ?line seq_trace:reset_trace(), - ?line Result = rpc:call(Node,?MODULE,stop_tracer,[1]), - ?line stop_node(Node), - ?line ok = io:format("~p~n",[Result]), - ?line [{0,{'receive',_,Self,Receiver,'receive'}}] = Result. - -trace_exit(doc) -> []; -trace_exit(suite) -> []; + receive after 1 -> ok end, + Self = self(), + seq_trace:reset_trace(), + Result = rpc:call(Node,?MODULE,stop_tracer,[1]), + stop_node(Node), + ok = io:format("~p~n",[Result]), + [{Label,{'receive',_,Self,Receiver,'receive'}, Ts}] = Result, + check_ts(TsType, Ts). + trace_exit(Config) when is_list(Config) -> - ?line seq_trace:reset_trace(), - ?line start_tracer(), - ?line Receiver = spawn_link(?MODULE, one_time_receiver, [exit]), - ?line process_flag(trap_exit, true), - ?line seq_trace:set_token(send,true), - ?line Receiver ! {before, exit}, + lists:foreach(fun do_trace_exit/1, ?TIMESTAMP_MODES). + +do_trace_exit(TsType) -> + seq_trace:reset_trace(), + start_tracer(), + Receiver = spawn_link(?MODULE, one_time_receiver, [exit]), + process_flag(trap_exit, true), + + %% Make sure complex labels survive the trip. + Label = make_ref(), + seq_trace:set_token(label,Label), + set_token_flags([send, TsType]), + + Receiver ! {before, exit}, %% let the other process receive the message: - ?line receive + receive {'EXIT', Receiver, {exit, {before, exit}}} -> seq_trace:set_token([]); Other -> seq_trace:set_token([]), - ?t:fail({received, Other}) + ct:fail({received, Other}) end, - ?line Self = self(), - ?line Result = stop_tracer(2), - ?line seq_trace:reset_trace(), - ?line ok = io:format("~p~n", [Result]), - ?line [{0, {send, {0,1}, Self, Receiver, {before, exit}}}, - {0, {send, {1,2}, Receiver, Self, - {'EXIT', Receiver, {exit, {before, exit}}}}}] = Result. - -distributed_exit(doc) -> []; -distributed_exit(suite) -> []; + Self = self(), + Result = stop_tracer(2), + seq_trace:reset_trace(), + ok = io:format("~p~n", [Result]), + [{Label, {send, {0,1}, Self, Receiver, {before, exit}}, Ts0}, + {Label, {send, {1,2}, Receiver, Self, + {'EXIT', Receiver, {exit, {before, exit}}}}, Ts1}] = Result, + check_ts(TsType, Ts0), + check_ts(TsType, Ts1). + distributed_exit(Config) when is_list(Config) -> - ?line {ok, Node} = start_node(seq_trace_other, []), - ?line {_, Dir} = code:is_loaded(?MODULE), - ?line Mdir = filename:dirname(Dir), - ?line true = rpc:call(Node, code, add_patha, [Mdir]), - ?line seq_trace:reset_trace(), - ?line rpc:call(Node, ?MODULE, start_tracer,[]), - ?line Receiver = spawn_link(Node, ?MODULE, one_time_receiver, [exit]), - ?line process_flag(trap_exit, true), - ?line seq_trace:set_token(send, true), - ?line Receiver ! {before, exit}, + lists:foreach(fun do_distributed_exit/1, ?TIMESTAMP_MODES). + +do_distributed_exit(TsType) -> + {ok, Node} = start_node(seq_trace_other, []), + {_, Dir} = code:is_loaded(?MODULE), + Mdir = filename:dirname(Dir), + true = rpc:call(Node, code, add_patha, [Mdir]), + seq_trace:reset_trace(), + rpc:call(Node, ?MODULE, start_tracer,[]), + Receiver = spawn_link(Node, ?MODULE, one_time_receiver, [exit]), + process_flag(trap_exit, true), + set_token_flags([send, TsType]), + Receiver ! {before, exit}, %% let the other process receive the message: - ?line receive + receive {'EXIT', Receiver, {exit, {before, exit}}} -> seq_trace:set_token([]); Other -> seq_trace:set_token([]), - ?t:fail({received, Other}) + ct:fail({received, Other}) end, - ?line Self = self(), - ?line Result = rpc:call(Node, ?MODULE, stop_tracer, [1]), - ?line seq_trace:reset_trace(), - ?line stop_node(Node), - ?line ok = io:format("~p~n", [Result]), - ?line [{0, {send, {1, 2}, Receiver, Self, - {'EXIT', Receiver, {exit, {before, exit}}}}}] = Result. + Self = self(), + Result = rpc:call(Node, ?MODULE, stop_tracer, [1]), + seq_trace:reset_trace(), + stop_node(Node), + ok = io:format("~p~n", [Result]), + [{0, {send, {1, 2}, Receiver, Self, + {'EXIT', Receiver, {exit, {before, exit}}}}, Ts}] = Result, + check_ts(TsType, Ts). + +label_capability_mismatch(Config) when is_list(Config) -> + Releases = ["20_latest"], + Available = [Rel || Rel <- Releases, test_server:is_release_available(Rel)], + case Available of + [] -> {skipped, "No incompatible releases available"}; + _ -> + lists:foreach(fun do_incompatible_labels/1, Available), + lists:foreach(fun do_compatible_labels/1, Available), + ok + end. + +do_incompatible_labels(Rel) -> + Cookie = atom_to_list(erlang:get_cookie()), + {ok, Node} = test_server:start_node( + list_to_atom(atom_to_list(?MODULE)++"_"++Rel), peer, + [{args, " -setcookie "++Cookie}, {erl, [{release, Rel}]}]), + + {_,Dir} = code:is_loaded(?MODULE), + Mdir = filename:dirname(Dir), + true = rpc:call(Node,code,add_patha,[Mdir]), + seq_trace:reset_trace(), + rpc:call(Node,?MODULE,start_tracer,[]), + Receiver = spawn(Node,?MODULE,one_time_receiver,[]), + + %% This node does not support arbitrary labels, so it must fail with a + %% timeout as the token is dropped silently. + seq_trace:set_token(label,make_ref()), + seq_trace:set_token('receive',true), + + Receiver ! 'receive', + %% let the other process receive the message: + receive after 10 -> ok end, + seq_trace:reset_trace(), + + {error,timeout} = rpc:call(Node,?MODULE,stop_tracer,[1]), + stop_node(Node), + ok. + +do_compatible_labels(Rel) -> + Cookie = atom_to_list(erlang:get_cookie()), + {ok, Node} = test_server:start_node( + list_to_atom(atom_to_list(?MODULE)++"_"++Rel), peer, + [{args, " -setcookie "++Cookie}, {erl, [{release, Rel}]}]), + + {_,Dir} = code:is_loaded(?MODULE), + Mdir = filename:dirname(Dir), + true = rpc:call(Node,code,add_patha,[Mdir]), + seq_trace:reset_trace(), + rpc:call(Node,?MODULE,start_tracer,[]), + Receiver = spawn(Node,?MODULE,one_time_receiver,[]), + + %% This node does not support arbitrary labels, but small integers should + %% still work. + Label = 1234, + seq_trace:set_token(label,Label), + seq_trace:set_token('receive',true), + + Receiver ! 'receive', + %% let the other process receive the message: + receive after 10 -> ok end, + Self = self(), + seq_trace:reset_trace(), + Result = rpc:call(Node,?MODULE,stop_tracer,[1]), + stop_node(Node), + ok = io:format("~p~n",[Result]), + [{Label,{'receive',_,Self,Receiver,'receive'}, _}] = Result, + ok. call(doc) -> "Tests special forms {is_seq_trace} and {get_seq_token} " "in trace match specs."; -call(suite) -> - []; call(Config) when is_list(Config) -> - ?line Self = self(), - ?line seq_trace:reset_trace(), - ?line TrA = transparent_tracer(), - ?line 1 = + Self = self(), + seq_trace:reset_trace(), + TrA = transparent_tracer(), + 1 = erlang:trace(Self, true, [call, set_on_spawn, {tracer, TrA(pid)}]), - ?line 1 = + 1 = erlang:trace_pattern({?MODULE, call_tracee_1, 1}, [{'_', [], [{message, {{{self}, {get_seq_token}}}}]}], [local]), - ?line 1 = + 1 = erlang:trace_pattern({?MODULE, call_tracee_2, 1}, [{'_', [{is_seq_trace}], [{message, {{{self}, {get_seq_token}}}}]}], [local]), - ?line RefA = make_ref(), - ?line Pid2A = spawn_link( + RefA = make_ref(), + Pid2A = spawn_link( fun() -> receive {_, msg, RefA} -> ok end, RefA = call_tracee_2(RefA), Self ! {self(), msg, RefA} end), - ?line Pid1A = spawn_link( + Pid1A = spawn_link( fun() -> receive {_, msg, RefA} -> ok end, RefA = call_tracee_1(RefA), Pid2A ! {self(), msg, RefA} end), - ?line Pid1A ! {Self, msg, RefA}, + Pid1A ! {Self, msg, RefA}, %% The message is passed Self -> Pid1B -> Pid2B -> Self. %% Traced functions are called in Pid1B and Pid2B. - ?line receive {Pid2A, msg, RefA} -> ok end, + receive {Pid2A, msg, RefA} -> ok end, %% Only call_tracee1 will be traced since the guard for %% call_tracee2 requires a sequential trace. The trace %% token is undefined. - ?line Token2A = [], - ?line {ok, [{trace, Pid1A, call, + Token2A = [], + {ok, [{trace, Pid1A, call, {?MODULE, call_tracee_1, [RefA]}, {Pid1A, Token2A}}]} = TrA({stop, 1}), - ?line seq_trace:reset_trace(), + seq_trace:reset_trace(), - ?line TrB = transparent_tracer(), - ?line 1 = + TrB = transparent_tracer(), + 1 = erlang:trace(Self, true, [call, set_on_spawn, {tracer, TrB(pid)}]), - ?line Label = 17, - ?line seq_trace:set_token(label, Label), % Token enters here!! - ?line RefB = make_ref(), - ?line Pid2B = spawn_link( + Label = 17, + seq_trace:set_token(label, Label), % Token enters here!! + RefB = make_ref(), + Pid2B = spawn_link( fun() -> receive {_, msg, RefB} -> ok end, RefB = call_tracee_2(RefB), Self ! {self(), msg, RefB} end), - ?line Pid1B = spawn_link( + Pid1B = spawn_link( fun() -> receive {_, msg, RefB} -> ok end, RefB = call_tracee_1(RefB), Pid2B ! {self(), msg, RefB} end), - ?line Pid1B ! {Self, msg, RefB}, + Pid1B ! {Self, msg, RefB}, %% The message is passed Self -> Pid1B -> Pid2B -> Self, and the %% seq_trace token follows invisibly. Traced functions are %% called in Pid1B and Pid2B. Seq_trace flags == 0 so no %% seq_trace messages are generated. - ?line receive {Pid2B, msg, RefB} -> ok end, + receive {Pid2B, msg, RefB} -> ok end, %% The values of these counters {.., 1, _, 0}, {.., 2, _, 1} %% depend on that seq_trace has been reset just before this test. - ?line Token1B = {0, Label, 1, Self, 0}, - ?line Token2B = {0, Label, 2, Pid1B, 1}, - ?line {ok, [{trace, Pid1B, call, + Token1B = {0, Label, 1, Self, 0}, + Token2B = {0, Label, 2, Pid1B, 1}, + {ok, [{trace, Pid1B, call, {?MODULE, call_tracee_1, [RefB]}, {Pid1B, Token1B}}, {trace, Pid2B, call, {?MODULE, call_tracee_2, [RefB]}, {Pid2B, Token2B}}]} = TrB({stop,2}), - ?line seq_trace:reset_trace(), + seq_trace:reset_trace(), ok. -port(doc) -> - "Send trace messages to a port."; -port(suite) -> []; +%% Send trace messages to a port. port(Config) when is_list(Config) -> - ?line Port = load_tracer(Config), - ?line seq_trace:set_system_tracer(Port), - - ?line seq_trace:set_token(print, true), - ?line Small = [small,term], - ?line seq_trace:print(0, Small), - ?line case get_port_message(Port) of - {seq_trace,0,{print,_,_,[],Small}} -> + lists:foreach(fun (TsType) -> do_port(TsType, Config) end, + ?TIMESTAMP_MODES). + +do_port(TsType, Config) -> + io:format("Testing ~p~n",[TsType]), + Port = load_tracer(Config), + seq_trace:set_system_tracer(Port), + + set_token_flags([print, TsType]), + Small = [small,term], + seq_trace:print(0, Small), + case get_port_message(Port) of + {seq_trace,0,{print,_,_,[],Small}} when TsType == no_timestamp -> + ok; + {seq_trace,0,{print,_,_,[],Small},Ts0} when TsType /= no_timestamp -> + check_ts(TsType, Ts0), ok; Other -> - ?line seq_trace:reset_trace(), - ?line ?t:fail({unexpected,Other}) + seq_trace:reset_trace(), + ct:fail({unexpected,Other}) end, %% OTP-4218 Messages from ports should not affect seq trace token. %% %% Check if trace token still is active on this process after %% the get_port_message/1 above that receives from a port. - ?line OtherSmall = [other | Small], - ?line seq_trace:print(0, OtherSmall), - ?line seq_trace:reset_trace(), - ?line case get_port_message(Port) of - {seq_trace,0,{print,_,_,[],OtherSmall}} -> + OtherSmall = [other | Small], + seq_trace:print(0, OtherSmall), + seq_trace:reset_trace(), + case get_port_message(Port) of + {seq_trace,0,{print,_,_,[],OtherSmall}} when TsType == no_timestamp -> + ok; + {seq_trace,0,{print,_,_,[],OtherSmall}, Ts1} when TsType /= no_timestamp -> + check_ts(TsType, Ts1), ok; Other1 -> - ?line ?t:fail({unexpected,Other1}) + ct:fail({unexpected,Other1}) end, - ?line seq_trace:set_token(print, true), - ?line Huge = huge_data(), - ?line seq_trace:print(0, Huge), - ?line seq_trace:reset_trace(), - ?line case get_port_message(Port) of + seq_trace:set_token(print, true), + Huge = huge_data(), + seq_trace:print(0, Huge), + seq_trace:reset_trace(), + case get_port_message(Port) of {seq_trace,0,{print,_,_,[],Huge}} -> ok; Other2 -> - ?line ?t:fail({unexpected,Other2}) + ct:fail({unexpected,Other2}) end, + unlink(Port), + exit(Port,kill), ok. get_port_message(Port) -> @@ -405,21 +525,19 @@ get_port_message(Port) -> {Port,{data,Bin}} when is_binary(Bin) -> binary_to_term(Bin); Other -> - ?t:fail({unexpected,Other}) + ct:fail({unexpected,Other}) after 5000 -> - ?t:fail(timeout) + ct:fail(timeout) end. -match_set_seq_token(suite) -> - []; match_set_seq_token(doc) -> ["Tests that match spec function set_seq_token does not " "corrupt the heap"]; match_set_seq_token(Config) when is_list(Config) -> - ?line Parent = self(), - ?line Timetrap = test_server:timetrap(test_server:seconds(20)), + Parent = self(), + %% OTP-4222 Match spec 'set_seq_token' corrupts heap %% %% This test crashes the emulator if the bug in question is present, @@ -427,13 +545,13 @@ match_set_seq_token(Config) when is_list(Config) -> %% %% All the timeout stuff is here to get decent accuracy of the error %% return value, instead of just 'timeout'. - % - ?line {ok, Sandbox} = start_node(seq_trace_other, []), - ?line true = rpc:call(Sandbox, code, add_patha, + %% + {ok, Sandbox} = start_node(seq_trace_other, []), + true = rpc:call(Sandbox, code, add_patha, [filename:dirname(code:which(?MODULE))]), - ?line Lbl = 4711, + Lbl = 4711, %% Do the possibly crashing test - ?line P1 = + P1 = spawn( fun () -> Parent ! {self(), @@ -441,16 +559,16 @@ match_set_seq_token(Config) when is_list(Config) -> ?MODULE, do_match_set_seq_token, [Lbl])} end), %% Probe the node with a simple rpc request, to see if it is alive. - ?line P2 = + P2 = spawn( fun () -> receive after 4000 -> ok end, Parent ! {self(), rpc:call(Sandbox, erlang, abs, [-1])} end), %% If the test node hangs completely, this timer expires. - ?line R3 = erlang:start_timer(8000, self(), void), + R3 = erlang:start_timer(8000, self(), void), %% - ?line {ok, Log} = + {ok, Log} = receive {P1, Result} -> exit(P2, done), @@ -465,10 +583,15 @@ match_set_seq_token(Config) when is_list(Config) -> exit(P2, timeout), {error, "Test node hung"} end, - ?line ok = check_match_set_seq_token_log(Lbl, Log), + + %% Sort the log on Pid, as events from different processes + %% are not guaranteed to arrive in a certain order to the + %% tracer + SortedLog = lists:keysort(2, Log), + + ok = check_match_set_seq_token_log(Lbl, SortedLog), %% - ?line stop_node(Sandbox), - ?line test_server:timetrap_cancel(Timetrap), + stop_node(Sandbox), ok. %% OTP-4222 Match spec 'set_seq_token' corrupts heap @@ -519,13 +642,13 @@ do_match_set_seq_token(Label) -> check_match_set_seq_token_log( Label, - [{trace,C,call,{?MODULE,countdown,[B,Ref]}, {0,Label,0,C,0}}, + [{trace,B,call,{?MODULE,bounce, [Ref]}, {0,Label,2,B,1}}, + {trace,B,call,{?MODULE,bounce, [Ref]}, {0,Label,4,B,3}}, + {trace,B,call,{?MODULE,bounce, [Ref]}, {0,Label,6,B,5}}, + {trace,C,call,{?MODULE,countdown,[B,Ref]}, {0,Label,0,C,0}}, {trace,C,call,{?MODULE,countdown,[B,Ref,3]},{0,Label,0,C,0}}, - {trace,B,call,{?MODULE,bounce, [Ref]}, {0,Label,2,B,1}}, {trace,C,call,{?MODULE,countdown,[B,Ref,2]},{0,Label,2,B,1}}, - {trace,B,call,{?MODULE,bounce, [Ref]}, {0,Label,4,B,3}}, {trace,C,call,{?MODULE,countdown,[B,Ref,1]},{0,Label,4,B,3}}, - {trace,B,call,{?MODULE,bounce, [Ref]}, {0,Label,6,B,5}}, {trace,C,call,{?MODULE,countdown,[B,Ref,0]},{0,Label,6,B,5}} ]) -> ok; @@ -560,14 +683,12 @@ bounce(Ref) -> -gc_seq_token(suite) -> - []; gc_seq_token(doc) -> ["Tests that a seq_trace token on a message in the inqueue ", "can be garbage collected."]; gc_seq_token(Config) when is_list(Config) -> - ?line Parent = self(), - ?line Timetrap = test_server:timetrap(test_server:seconds(20)), + Parent = self(), + %% OTP-4555 Seq trace token causes free mem read in gc %% %% This test crashes the emulator if the bug in question is present, @@ -575,13 +696,13 @@ gc_seq_token(Config) when is_list(Config) -> %% %% All the timeout stuff is here to get decent accuracy of the error %% return value, instead of just 'timeout'. - % - ?line {ok, Sandbox} = start_node(seq_trace_other, []), - ?line true = rpc:call(Sandbox, code, add_patha, + %% + {ok, Sandbox} = start_node(seq_trace_other, []), + true = rpc:call(Sandbox, code, add_patha, [filename:dirname(code:which(?MODULE))]), - ?line Label = 4711, + Label = 4711, %% Do the possibly crashing test - ?line P1 = + P1 = spawn( fun () -> Parent ! {self(), @@ -589,16 +710,16 @@ gc_seq_token(Config) when is_list(Config) -> ?MODULE, do_gc_seq_token, [Label])} end), %% Probe the node with a simple rpc request, to see if it is alive. - ?line P2 = + P2 = spawn( fun () -> receive after 4000 -> ok end, Parent ! {self(), rpc:call(Sandbox, erlang, abs, [-1])} end), %% If the test node hangs completely, this timer expires. - ?line R3 = erlang:start_timer(8000, self(), void), + R3 = erlang:start_timer(8000, self(), void), %% - ?line ok = + ok = receive {P1, Result} -> exit(P2, done), @@ -614,8 +735,7 @@ gc_seq_token(Config) when is_list(Config) -> {error, "Test node hung"} end, %% - ?line stop_node(Sandbox), - ?line test_server:timetrap_cancel(Timetrap), + stop_node(Sandbox), ok. do_gc_seq_token(Label) -> @@ -663,6 +783,24 @@ do_shrink(N) -> erlang:garbage_collect(), do_shrink(N-1). +%% Test that messages from a port does not clear the token +port_clean_token(Config) when is_list(Config) -> + seq_trace:reset_trace(), + Label = make_ref(), + seq_trace:set_token(label, Label), + {label,Label} = seq_trace:get_token(label), + + %% Create a port and get messages from it + %% We use os:cmd as a convenience as it does + %% open_port, port_command, port_close and receives replies. + %% Maybe it is not ideal to rely on the internal implementation + %% of os:cmd but it will have to do. + os:cmd("ls"), + + %% Make sure that the seq_trace token is still there + {label,Label} = seq_trace:get_token(label), + + ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -733,7 +871,7 @@ simple_tracer(Data, DN) -> {seq_trace,Label,Info,Ts} -> simple_tracer([{Label,Info,Ts}|Data], DN+1); {seq_trace,Label,Info} -> - simple_tracer([{Label,Info}|Data], DN+1); + simple_tracer([{Label,Info, no_timestamp}|Data], DN+1); {stop,N,From} when DN >= N -> From ! {tracerlog,lists:reverse(Data)} end. @@ -758,8 +896,55 @@ start_tracer() -> seq_trace:set_system_tracer(Pid), Pid. - - +set_token_flags([]) -> + ok; +set_token_flags([no_timestamp|Flags]) -> + seq_trace:set_token(timestamp, false), + seq_trace:set_token(monotonic_timestamp, false), + seq_trace:set_token(strict_monotonic_timestamp, false), + set_token_flags(Flags); +set_token_flags([Flag|Flags]) -> + seq_trace:set_token(Flag, true), + set_token_flags(Flags). + +check_ts(no_timestamp, Ts) -> + try + no_timestamp = Ts + catch + _ : _ -> + ct:fail({unexpected_timestamp, Ts}) + end, + ok; +check_ts(timestamp, Ts) -> + try + {Ms,S,Us} = Ts, + true = is_integer(Ms), + true = is_integer(S), + true = is_integer(Us) + catch + _ : _ -> + ct:fail({unexpected_timestamp, Ts}) + end, + ok; +check_ts(monotonic_timestamp, Ts) -> + try + true = is_integer(Ts) + catch + _ : _ -> + ct:fail({unexpected_timestamp, Ts}) + end, + ok; +check_ts(strict_monotonic_timestamp, Ts) -> + try + {MT, UMI} = Ts, + true = is_integer(MT), + true = is_integer(UMI) + catch + _ : _ -> + ct:fail({unexpected_timestamp, Ts}) + end, + ok. + start_node(Name, Param) -> test_server:start_node(Name, slave, [{args, Param}]). @@ -767,7 +952,7 @@ stop_node(Node) -> test_server:stop_node(Node). load_tracer(Config) -> - Path = ?config(data_dir, Config), + Path = proplists:get_value(data_dir, Config), ok = erl_ddll:load_driver(Path, echo_drv), open_port({spawn,echo_drv}, [eof,binary]). diff --git a/lib/kernel/test/standard_error_SUITE.erl b/lib/kernel/test/standard_error_SUITE.erl index b290454b40..1d9026dc58 100644 --- a/lib/kernel/test/standard_error_SUITE.erl +++ b/lib/kernel/test/standard_error_SUITE.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2014. All Rights Reserved. +%% Copyright Ericsson AB 2014-2016. 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/. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at %% -%% 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. +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% @@ -20,13 +21,13 @@ -module(standard_error_SUITE). -export([all/0,suite/0]). --export([badarg/1,getopts/1]). +-export([badarg/1,getopts/1,output/1]). suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> - [badarg,getopts]. + [badarg,getopts,output]. badarg(Config) when is_list(Config) -> {'EXIT',{badarg,_}} = (catch io:put_chars(standard_error, [oops])), @@ -36,3 +37,30 @@ badarg(Config) when is_list(Config) -> getopts(Config) when is_list(Config) -> [{encoding,latin1}] = io:getopts(standard_error), ok. + +%% Test that writing a lot of output to standard_error does not cause the +%% processes handling it to terminate like this: +%% +%% =ERROR REPORT==== 9-Aug-2015::23:19:23 === +%% ** Generic server standard_error_sup terminating +%% ** Last message in was {'EXIT',<0.28.0>,eagain} +%% ** When Server state == {state,standard_error,undefined,<0.28.0>, +%% {local,standard_error_sup}} +%% ** Reason for termination == +%% ** eagain +%% +%% This problem, observed with Erlang 18.0.2, was fixed in fd_driver by +%% properly handling EAGAIN if it arises on file descriptor writes. +%% +output(Config) when is_list(Config) -> + Ref = monitor(process, standard_error_sup), + Chars = [ [["1234567890" || _ <- lists:seq(1,10)], $\s, + integer_to_list(L), $\r, $\n] || L <- lists:seq(1, 100) ], + ok = io:put_chars(standard_error, Chars), + receive + {'DOWN', Ref, process, _, _} -> + error(standard_error_noproc) + after + 500 -> + ok + end. diff --git a/lib/kernel/test/topApp.erl b/lib/kernel/test/topApp.erl index f44e99f738..6ea957fdc1 100644 --- a/lib/kernel/test/topApp.erl +++ b/lib/kernel/test/topApp.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2010. All Rights Reserved. +%% Copyright Ericsson AB 1998-2016. 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/lib/kernel/test/topApp2.erl b/lib/kernel/test/topApp2.erl index b791d4a914..aeb024f7a7 100644 --- a/lib/kernel/test/topApp2.erl +++ b/lib/kernel/test/topApp2.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2010. All Rights Reserved. +%% Copyright Ericsson AB 1998-2016. 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/lib/kernel/test/topApp3.erl b/lib/kernel/test/topApp3.erl index 456ef5b2fb..917da3dad4 100644 --- a/lib/kernel/test/topApp3.erl +++ b/lib/kernel/test/topApp3.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2010. All Rights Reserved. +%% Copyright Ericsson AB 1998-2016. 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% diff --git a/lib/kernel/test/wrap_log_reader_SUITE.erl b/lib/kernel/test/wrap_log_reader_SUITE.erl index 16b3a7cc1e..59b088ca73 100644 --- a/lib/kernel/test/wrap_log_reader_SUITE.erl +++ b/lib/kernel/test/wrap_log_reader_SUITE.erl @@ -1,25 +1,26 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2012. All Rights Reserved. +%% Copyright Ericsson AB 1998-2018. 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/. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at %% -%% 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. +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% -module(wrap_log_reader_SUITE). -%-define(debug, true). +%%-define(debug, true). -ifdef(debug). -define(format(S, A), io:format(S, A)). @@ -28,9 +29,9 @@ -define(config(X,Y), foo). -define(t,test_server). -else. --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -define(format(S, A), ok). --define(privdir(Conf), ?config(priv_dir, Conf)). +-define(privdir(Conf), proplists:get_value(priv_dir, Conf)). -endif. -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, @@ -46,7 +47,9 @@ -export([init_per_testcase/2, end_per_testcase/2]). -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap,{minutes,1}}]. all() -> [no_file, {group, one}, {group, two}, {group, four}, @@ -70,75 +73,70 @@ end_per_group(_GroupName, Config) -> Config. -init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) -> - Dog=?t:timetrap(?t:seconds(60)), - [{watchdog, Dog} | Config]. +init_per_testcase(Func, Config) -> + Config. end_per_testcase(_Func, _Config) -> - Dog=?config(watchdog, _Config), - ?t:timetrap_cancel(Dog). + ok. -no_file(suite) -> []; -no_file(doc) -> ["No log file exists"]; +%% No log file exists. no_file(Conf) when is_list(Conf) -> - ?line code:add_path(?config(data_dir,Conf)), + code:add_path(proplists:get_value(data_dir,Conf)), Dir = ?privdir(Conf), File = join(Dir, "sune.LOG"), delete_files(File), start(), wlt ! {open, self(), File}, - ?line rec({error, {index_file_not_found, File}}, ?LINE), + rec({error, {index_file_not_found, File}}, ?LINE), wlt ! {open, self(), File, 1}, - ?line rec({error, {index_file_not_found, File}}, ?LINE), + rec({error, {index_file_not_found, File}}, ?LINE), wlt ! {open, self(), File, 4}, - ?line rec({error, {index_file_not_found, File}}, ?LINE), + rec({error, {index_file_not_found, File}}, ?LINE), stop(), delete_files(File), ok. -one_empty(suite) -> []; -one_empty(doc) -> ["One empty index file"]; +%% One empty index file. one_empty(Conf) when is_list(Conf) -> Dir = ?privdir(Conf), File = join(Dir, "sune.LOG"), delete_files(File), start(), - ?line open(sune, File, ?LINE), + open(sune, File, ?LINE), %% open - ?line do_chunk([{open,File}, eof], wlt, ?LINE), - ?line do_chunk([{open,File,1}, eof], wlt, ?LINE), + do_chunk([{open,File}, eof], wlt, ?LINE), + do_chunk([{open,File,1}, eof], wlt, ?LINE), wlt ! {open, self(), File, 2}, - ?line rec({error, {file_not_found, add_ext(File, 2)}}, ?LINE), - ?line close(sune), + rec({error, {file_not_found, add_ext(File, 2)}}, ?LINE), + close(sune), %% closed - ?line do_chunk([{open,File}, eof], wlt, ?LINE), - ?line do_chunk([{open,File,1}, eof], wlt, ?LINE), + do_chunk([{open,File}, eof], wlt, ?LINE), + do_chunk([{open,File,1}, eof], wlt, ?LINE), wlt ! {open, self(), File, 2}, - ?line rec({error, {file_not_found, add_ext(File, 2)}}, ?LINE), + rec({error, {file_not_found, add_ext(File, 2)}}, ?LINE), stop(), delete_files(File), ok. -one_filled(suite) -> []; -one_filled(doc) -> ["One filled index file"]; +%% One filled index file. one_filled(Conf) when is_list(Conf) -> Dir = ?privdir(Conf), File = join(Dir, "sune.LOG"), delete_files(File), start(), - ?line open(sune, File, ?LINE), - ?line log_terms(sune, ["first round, one", "first round, two"]), - ?line sync(sune), + open(sune, File, ?LINE), + log_terms(sune, ["first round, one", "first round, two"]), + sync(sune), %% open test_one(File), - ?line close(sune), + close(sune), %% closed test_one(File), @@ -147,34 +145,33 @@ one_filled(Conf) when is_list(Conf) -> ok. test_one(File) -> - ?line do_chunk([{open,File}, - {chunk, ["first round, one", "first round, two"]}, - eof], wlt, ?LINE), - ?line do_chunk([{open,File,1}, - {chunk, ["first round, one", "first round, two"]}, - eof], wlt, ?LINE), + do_chunk([{open,File}, + {chunk, ["first round, one", "first round, two"]}, + eof], wlt, ?LINE), + do_chunk([{open,File,1}, + {chunk, ["first round, one", "first round, two"]}, + eof], wlt, ?LINE), wlt ! {open, self(), File, 2}, - ?line rec({error, {file_not_found, add_ext(File, 2)}}, ?LINE), - ?line do_chunk([{open,File,1}, {chunk, 1, ["first round, one"]}, - {chunk, 1, ["first round, two"]}, eof], wlt, ?LINE), + rec({error, {file_not_found, add_ext(File, 2)}}, ?LINE), + do_chunk([{open,File,1}, {chunk, 1, ["first round, one"]}, + {chunk, 1, ["first round, two"]}, eof], wlt, ?LINE), ok. -two_filled(suite) -> []; -two_filled(doc) -> ["Two filled index files"]; +%% Two filled index files. two_filled(Conf) when is_list(Conf) -> Dir = ?privdir(Conf), File = list_to_atom(join(Dir, "sune.LOG")), delete_files(File), start(), - ?line open(sune, File, ?LINE), - ?line log_terms(sune, ["first round, 11", "first round, 12"]), - ?line log_terms(sune, ["first round, 21", "first round, 22"]), - ?line sync(sune), + open(sune, File, ?LINE), + log_terms(sune, ["first round, 11", "first round, 12"]), + log_terms(sune, ["first round, 21", "first round, 22"]), + sync(sune), %% open test_two(File), - ?line close(sune), + close(sune), %% closed test_two(File), @@ -183,37 +180,36 @@ two_filled(Conf) when is_list(Conf) -> ok. test_two(File) -> - ?line do_chunk([{open,File}, - {chunk, infinity, ["first round, 11", "first round, 12"]}, - {chunk, ["first round, 21", "first round, 22"]}, - eof], wlt, ?LINE), - ?line do_chunk([{open,File,1}, - {chunk, ["first round, 11", "first round, 12"]}, - eof], wlt, ?LINE), - ?line do_chunk([{open,File,2}, - {chunk, ["first round, 21", "first round, 22"]}, - eof], wlt, ?LINE), + do_chunk([{open,File}, + {chunk, infinity, ["first round, 11", "first round, 12"]}, + {chunk, ["first round, 21", "first round, 22"]}, + eof], wlt, ?LINE), + do_chunk([{open,File,1}, + {chunk, ["first round, 11", "first round, 12"]}, + eof], wlt, ?LINE), + do_chunk([{open,File,2}, + {chunk, ["first round, 21", "first round, 22"]}, + eof], wlt, ?LINE), wlt ! {open, self(), File, 3}, - ?line rec({error, {file_not_found, add_ext(File, 3)}}, ?LINE), - ?line do_chunk([{open,File,1}, {chunk, 1, ["first round, 11"]}, - {chunk, 2, ["first round, 12"]}, eof], wlt, ?LINE), + rec({error, {file_not_found, add_ext(File, 3)}}, ?LINE), + do_chunk([{open,File,1}, {chunk, 1, ["first round, 11"]}, + {chunk, 2, ["first round, 12"]}, eof], wlt, ?LINE), ok. -four_filled(suite) -> []; -four_filled(doc) -> ["Four filled index files"]; +%% Four filled index files. four_filled(Conf) when is_list(Conf) -> Dir = ?privdir(Conf), File = join(Dir, "sune.LOG"), delete_files(File), start(), - ?line open(sune, File, ?LINE), - ?line init_files(0), - ?line sync(sune), + open(sune, File, ?LINE), + init_files(0), + sync(sune), %% open test_four(File), - ?line close(sune), + close(sune), %% closed test_four(File), @@ -222,42 +218,41 @@ four_filled(Conf) when is_list(Conf) -> ok. test_four(File) -> - ?line do_chunk([{open,File}, - {chunk, ["first round, 11", "first round, 12"]}, - {chunk, ["first round, 21", "first round, 22"]}, - {chunk, ["first round, 31", "first round, 32"]}, - {chunk, ["first round, 41", "first round, 42"]}, - eof], wlt, ?LINE), - ?line do_chunk([{open,File,1}, - {chunk, ["first round, 11", "first round, 12"]}, - eof], wlt, ?LINE), - ?line do_chunk([{open,File,4}, - {chunk, ["first round, 41", "first round, 42"]}, - eof], wlt, ?LINE), + do_chunk([{open,File}, + {chunk, ["first round, 11", "first round, 12"]}, + {chunk, ["first round, 21", "first round, 22"]}, + {chunk, ["first round, 31", "first round, 32"]}, + {chunk, ["first round, 41", "first round, 42"]}, + eof], wlt, ?LINE), + do_chunk([{open,File,1}, + {chunk, ["first round, 11", "first round, 12"]}, + eof], wlt, ?LINE), + do_chunk([{open,File,4}, + {chunk, ["first round, 41", "first round, 42"]}, + eof], wlt, ?LINE), wlt ! {open, self(), File, 5}, - ?line rec({error, {file_not_found, add_ext(File, 5)}}, ?LINE), - ?line do_chunk([{open,File,1}, {chunk, 1, ["first round, 11"]}, - {chunk, 2, ["first round, 12"]}, eof], wlt, ?LINE), - ?line do_chunk([{open,File,4}, {chunk, 1, ["first round, 41"]}, - {chunk, 2, ["first round, 42"]}, eof], wlt, ?LINE), + rec({error, {file_not_found, add_ext(File, 5)}}, ?LINE), + do_chunk([{open,File,1}, {chunk, 1, ["first round, 11"]}, + {chunk, 2, ["first round, 12"]}, eof], wlt, ?LINE), + do_chunk([{open,File,4}, {chunk, 1, ["first round, 41"]}, + {chunk, 2, ["first round, 42"]}, eof], wlt, ?LINE), ok. -wrap_filled(suite) -> []; -wrap_filled(doc) -> ["First wrap, open, filled index file"]; +%% First wrap, open, filled index file. wrap_filled(Conf) when is_list(Conf) -> Dir = ?privdir(Conf), File = join(Dir, "sune.LOG"), delete_files(File), start(), - ?line open(sune, File, ?LINE), - ?line init_files(0), - ?line log_terms(sune, ["second round, 11", "second round, 12"]), - ?line sync(sune), + open(sune, File, ?LINE), + init_files(0), + log_terms(sune, ["second round, 11", "second round, 12"]), + sync(sune), %% open test_wrap(File), - ?line close(sune), + close(sune), %% closed test_wrap(File), @@ -266,103 +261,100 @@ wrap_filled(Conf) when is_list(Conf) -> ok. test_wrap(File) -> - ?line do_chunk([{open,File}, - {chunk, ["first round, 21", "first round, 22"]}, - {chunk, ["first round, 31", "first round, 32"]}, - {chunk, ["first round, 41", "first round, 42"]}, - {chunk, ["second round, 11", "second round, 12"]}, - eof], wlt, ?LINE), - ?line do_chunk([{open,File,1}, - {chunk, ["second round, 11", "second round, 12"]}, - eof], wlt, ?LINE), - ?line do_chunk([{open,File,2}, - {chunk, ["first round, 21", "first round, 22"]}, - eof], wlt, ?LINE), + do_chunk([{open,File}, + {chunk, ["first round, 21", "first round, 22"]}, + {chunk, ["first round, 31", "first round, 32"]}, + {chunk, ["first round, 41", "first round, 42"]}, + {chunk, ["second round, 11", "second round, 12"]}, + eof], wlt, ?LINE), + do_chunk([{open,File,1}, + {chunk, ["second round, 11", "second round, 12"]}, + eof], wlt, ?LINE), + do_chunk([{open,File,2}, + {chunk, ["first round, 21", "first round, 22"]}, + eof], wlt, ?LINE), wlt ! {open, self(), File, 5}, - ?line rec({error, {file_not_found, add_ext(File, 5)}}, ?LINE), - ?line do_chunk([{open,File,1}, {chunk, 1, ["second round, 11"]}, - {chunk, 2, ["second round, 12"]}, eof], wlt, ?LINE), - ?line do_chunk([{open,File,4}, {chunk, 1, ["first round, 41"]}, - {chunk, 2, ["first round, 42"]}, eof], wlt, ?LINE), + rec({error, {file_not_found, add_ext(File, 5)}}, ?LINE), + do_chunk([{open,File,1}, {chunk, 1, ["second round, 11"]}, + {chunk, 2, ["second round, 12"]}, eof], wlt, ?LINE), + do_chunk([{open,File,4}, {chunk, 1, ["first round, 41"]}, + {chunk, 2, ["first round, 42"]}, eof], wlt, ?LINE), ok. -wrapping(suite) -> []; -wrapping(doc) -> ["Wrapping at the same time as reading"]; +%% Wrapping at the same time as reading. wrapping(Conf) when is_list(Conf) -> Dir = ?privdir(Conf), File = join(Dir, "sune.LOG"), delete_files(File), start(), - ?line open(sune, File, ?LINE), - ?line init_files(1100), - ?line sync(sune), - ?line C1 = + open(sune, File, ?LINE), + init_files(1100), + sync(sune), + C1 = do_chunk([{open,File}, {chunk, 1, ["first round, 11"]}], wlt, ?LINE), - ?line log_terms(sune, ["second round, 11", "second round, 12"]), - ?line sync(sune), - ?line do_chunk([{chunk, 1, ["first round, 12"]}, - %% Here two bad bytes are found. - {chunk, ["first round, 21", "first round, 22"]}, - {chunk, ["first round, 31", "first round, 32"]}, - {chunk, ["first round, 41", "first round, 42"]}, eof], - wlt, ?LINE, C1), + log_terms(sune, ["second round, 11", "second round, 12"]), + sync(sune), + do_chunk([{chunk, 1, ["first round, 12"]}, + %% Here two bad bytes are found. + {chunk, ["first round, 21", "first round, 22"]}, + {chunk, ["first round, 31", "first round, 32"]}, + {chunk, ["first round, 41", "first round, 42"]}, eof], + wlt, ?LINE, C1), start(), delete_files(File), - ?line open(sune, File, ?LINE), - ?line init_files(1100), - ?line sync(sune), - ?line C2 = + open(sune, File, ?LINE), + init_files(1100), + sync(sune), + C2 = do_chunk([{open,File}, {chunk, 1, ["first round, 11"]}], wlt, ?LINE), - ?line log_terms(sune, ["second round, 11", "second round, 12"]), - ?line close(sune), - ?line do_chunk([{chunk, 1, ["first round, 12"]}, - %% Here two bad bytes are found. - {chunk, ["first round, 21", "first round, 22"]}, - {chunk, ["first round, 31", "first round, 32"]}, - {chunk, ["first round, 41", "first round, 42"]}, eof], - wlt, ?LINE, C2), + log_terms(sune, ["second round, 11", "second round, 12"]), + close(sune), + do_chunk([{chunk, 1, ["first round, 12"]}, + %% Here two bad bytes are found. + {chunk, ["first round, 21", "first round, 22"]}, + {chunk, ["first round, 31", "first round, 32"]}, + {chunk, ["first round, 41", "first round, 42"]}, eof], + wlt, ?LINE, C2), start(), delete_files(File), - ?line open(sune, File, ?LINE), - ?line init_files(1100), - ?line sync(sune), - ?line C3 = do_chunk([{open,File}], wlt, ?LINE), - ?line log_terms(sune, ["second round, 11"]), - ?line sync(sune), - ?line do_chunk([{chunk, 1, ["second round, 11"]}, - {chunk, 1, ["first round, 21"]}, - {chunk, 1, ["first round, 22"]}, - {chunk, ["first round, 31", "first round, 32"]}, - {chunk, ["first round, 41", "first round, 42"]}, eof], - wlt, ?LINE, C3), + open(sune, File, ?LINE), + init_files(1100), + sync(sune), + C3 = do_chunk([{open,File}], wlt, ?LINE), + log_terms(sune, ["second round, 11"]), + sync(sune), + do_chunk([{chunk, 1, ["second round, 11"]}, + {chunk, 1, ["first round, 21"]}, + {chunk, 1, ["first round, 22"]}, + {chunk, ["first round, 31", "first round, 32"]}, + {chunk, ["first round, 41", "first round, 42"]}, eof], + wlt, ?LINE, C3), stop(), delete_files(File), ok. -external(suite) -> []; -external(doc) -> ["External format"]; +%% External format. external(Conf) when is_list(Conf) -> Dir = ?privdir(Conf), File = join(Dir, "sune.LOG"), delete_files(File), start(), - ?line open_ext(sune, File, ?FILE), - ?line init_files_ext(0), - ?line close(sune), + open_ext(sune, File, ?FILE), + init_files_ext(0), + close(sune), P0 = pps(), wlt ! {open, self(), File}, - ?line rec({error, {not_a_log_file, add_ext(File, 1)}}, ?LINE), - ?line true = (P0 == pps()), + rec({error, {not_a_log_file, add_ext(File, 1)}}, ?LINE), + true = (P0 == pps()), stop(), delete_files(File), ok. -error(suite) -> []; -error(doc) -> ["Error situations"]; +%% Error situations. error(Conf) when is_list(Conf) -> Dir = ?privdir(Conf), File = join(Dir, "sune.LOG"), @@ -371,78 +363,79 @@ error(Conf) when is_list(Conf) -> P0 = pps(), wlt ! {open, self(), File, 1}, - ?line rec({error, {index_file_not_found, File}}, ?LINE), + rec({error, {index_file_not_found, File}}, ?LINE), wlt ! {open, self(), File}, - ?line rec({error, {index_file_not_found, File}}, ?LINE), - ?line true = (P0 == pps()), + rec({error, {index_file_not_found, File}}, ?LINE), + true = (P0 == pps()), - ?line open(sune, File, ?LINE), - ?line close(sune), + open(sune, File, ?LINE), + close(sune), P1 = pps(), - ?line First = add_ext(File, 1), - ?line ok = file:delete(First), + First = add_ext(File, 1), + ok = file:delete(First), wlt ! {open, self(), File}, - ?line rec({error, {not_a_log_file, First}}, ?LINE), - ?line true = (P1 == pps()), + rec({error, {not_a_log_file, First}}, ?LINE), + true = (P1 == pps()), delete_files(File), - ?line open(sune, File, ?LINE), - ?line init_files(0), - ?line close(sune), + open(sune, File, ?LINE), + init_files(0), + close(sune), P2 = pps(), - ?line C = do_chunk([{open,File}, - {chunk, ["first round, 11", "first round, 12"]}], - wlt, ?LINE), - ?line Second = add_ext(File, 2), - ?line ok = file:delete(Second), + C = do_chunk([{open,File}, + {chunk, ["first round, 11", "first round, 12"]}], + wlt, ?LINE), + Second = add_ext(File, 2), + ok = file:delete(Second), wlt ! {chunk, self(), C}, - ?line rec({error, {file_error, Second, {error, enoent}}}, ?LINE), - ?line ok = file:write_file(Second, <<17:(3*8)>>), % three bytes + rec({error, {file_error, Second, {error, enoent}}}, ?LINE), + ok = file:write_file(Second, <<17:(3*8)>>), % three bytes wlt ! {chunk, self(), C}, - ?line rec({error, {not_a_log_file, Second}}, ?LINE), - ?line do_chunk([close], wlt, ?LINE, C), - ?line true = (P2 == pps()), + rec({error, {not_a_log_file, Second}}, ?LINE), + do_chunk([close], wlt, ?LINE, C), + true = (P2 == pps()), delete_files(File), - ?line open(sune, File, ?LINE), - ?line init_files(0), - ?line close(sune), + open(sune, File, ?LINE), + init_files(0), + close(sune), P3 = pps(), timer:sleep(1100), Now = calendar:local_time(), - ?line ok = file:change_time(First, Now), - ?line C2 = do_chunk([{open,File}, - {chunk, ["first round, 11", "first round, 12"]}], - wlt, ?LINE), + ok = file:change_time(First, Now), + C2 = do_chunk([{open,File}, + {chunk, ["first round, 11", "first round, 12"]}], + wlt, ?LINE), wlt ! {chunk, self(), C2}, - ?line rec({error,{is_wrapped,First}}, ?LINE), - ?line do_chunk([close], wlt, ?LINE, C2), + rec({error,{is_wrapped,First}}, ?LINE), + do_chunk([close], wlt, ?LINE, C2), IndexFile = add_ext(File, idx), - ?line ok = file:write_file(IndexFile, <<17:(3*8)>>), + ok = file:write_file(IndexFile, <<17:(3*8)>>), wlt ! {open, self(), File, 1}, - ?line rec({error, {index_file_not_found, File}}, ?LINE), - ?line true = (P3 == pps()), + rec({error, {index_file_not_found, File}}, ?LINE), + true = (P3 == pps()), stop(), delete_files(File), ok. start() -> - ?line ok = wrap_log_test:stop(), + ok = wrap_log_test:stop(), dl_wait(), - ?line ok = wrap_log_test:init(). + ok = wrap_log_test:init(). stop() -> - ?line ok = wrap_log_test:stop(), + ok = wrap_log_test:stop(), dl_wait(). -%% Give disk logs opened by 'logger' and 'wlt' time to close after +%% Give disk logs opened by 'wlr_logger' and 'wlt' time to close after %% receiving EXIT signals. dl_wait() -> case disk_log:accessible_logs() of {[], []} -> ok; - _ -> + _X -> + erlang:display(_X), timer:sleep(100), dl_wait() end. @@ -457,24 +450,24 @@ delete_files(File) -> ok. init_files(Delay) -> - ?line log_terms(sune, ["first round, 11", "first round, 12"]), + log_terms(sune, ["first round, 11", "first round, 12"]), timer:sleep(Delay), - ?line log_terms(sune, ["first round, 21", "first round, 22"]), + log_terms(sune, ["first round, 21", "first round, 22"]), timer:sleep(Delay), - ?line log_terms(sune, ["first round, 31", "first round, 32"]), + log_terms(sune, ["first round, 31", "first round, 32"]), timer:sleep(Delay), - ?line log_terms(sune, ["first round, 41", "first round, 42"]), + log_terms(sune, ["first round, 41", "first round, 42"]), timer:sleep(Delay), ok. init_files_ext(Delay) -> - ?line blog_terms(sune, ["first round, 11", "first round, 12"]), + blog_terms(sune, ["first round, 11", "first round, 12"]), timer:sleep(Delay), - ?line blog_terms(sune, ["first round, 21", "first round, 22"]), + blog_terms(sune, ["first round, 21", "first round, 22"]), timer:sleep(Delay), - ?line blog_terms(sune, ["first round, 31", "first round, 32"]), + blog_terms(sune, ["first round, 31", "first round, 32"]), timer:sleep(Delay), - ?line blog_terms(sune, ["first round, 41", "first round, 42"]), + blog_terms(sune, ["first round, 41", "first round, 42"]), timer:sleep(Delay), ok. @@ -486,27 +479,27 @@ do_chunk(Commands, Server, Where) -> do_chunk([{open, File, One} | Cs], S, W, _C) -> S ! {open, self(), File, One}, - ?line NC = rec1(ok, {W,?LINE}), + NC = rec1(ok, {W,?LINE}), do_chunk(Cs, S, W, NC); do_chunk([{open, File} | Cs], S, W, _C) -> S ! {open, self(), File}, - ?line NC = rec1(ok, {W,?LINE}), + NC = rec1(ok, {W,?LINE}), do_chunk(Cs, S, W, NC); do_chunk([{chunk, Terms} | Cs], S, W, C) -> S ! {chunk, self(), C}, - ?line NC = rec2(Terms, {W,?LINE}), + NC = rec2(Terms, {W,?LINE}), do_chunk(Cs, S, W, NC); do_chunk([{chunk, N, Terms} | Cs], S, W, C) -> S ! {chunk, self(), C, N}, - ?line NC = rec2(Terms, {W,?LINE}), + NC = rec2(Terms, {W,?LINE}), do_chunk(Cs, S, W, NC); do_chunk([eof], S, W, C) -> S ! {chunk, self(), C}, - ?line C1 = rec2(eof, {W,?LINE}), + C1 = rec2(eof, {W,?LINE}), do_chunk([close], S, W, C1); do_chunk([close], S, W, C) -> S ! {close, self(), C}, - ?line rec(ok, {W,?LINE}); + rec(ok, {W,?LINE}); do_chunk([], _S, _W, C) -> C. @@ -515,50 +508,50 @@ add_ext(Name, Ext) -> %% disk_log. open(Log, File, Where) -> - logger ! {open, self(), Log, File}, + wlr_logger ! {open, self(), Log, File}, rec1(ok, Where). open_ext(Log, File, Where) -> - logger ! {open_ext, self(), Log, File}, + wlr_logger ! {open_ext, self(), Log, File}, rec1(ok, Where). close(Log) -> - logger ! {close, self(), Log}, + wlr_logger ! {close, self(), Log}, rec(ok, ?LINE). sync(Log) -> - logger ! {sync, self(), Log}, + wlr_logger ! {sync, self(), Log}, rec(ok, ?LINE). log_terms(File, Terms) -> - logger ! {log_terms, self(), File, Terms}, + wlr_logger ! {log_terms, self(), File, Terms}, rec(ok, ?LINE). blog_terms(File, Terms) -> - logger ! {blog_terms, self(), File, Terms}, + wlr_logger ! {blog_terms, self(), File, Terms}, rec(ok, ?LINE). rec1(M, Where) -> receive {M, C} -> C; - Else -> test_server:fail({error, {Where, Else}}) - after 1000 -> test_server:fail({error, {Where, time_out}}) + Else -> ct:fail({error, {Where, Else}}) + after 1000 -> ct:fail({error, {Where, time_out}}) end. rec2(M, Where) -> receive {C, M} -> C; - Else -> test_server:fail({error, {Where, Else}}) - after 1000 -> test_server:fail({error, {Where, time_out}}) + Else -> ct:fail({error, {Where, Else}}) + after 1000 -> ct:fail({error, {Where, time_out}}) end. rec(M, Where) -> receive M -> ok; - Else -> ?t:fail({error, {Where, Else}}) - after 5000 -> ?t:fail({error, {Where, time_out}}) + Else -> ct:fail({error, {Where, Else}}) + after 5000 -> ct:fail({error, {Where, time_out}}) end. - + pps() -> {erlang:ports(), lists:filter(fun erlang:is_process_alive/1, processes())}. diff --git a/lib/kernel/test/wrap_log_reader_SUITE_data/wrap_log_test.erl b/lib/kernel/test/wrap_log_reader_SUITE_data/wrap_log_test.erl index e5ff70fd49..d2bac40192 100644 --- a/lib/kernel/test/wrap_log_reader_SUITE_data/wrap_log_test.erl +++ b/lib/kernel/test/wrap_log_reader_SUITE_data/wrap_log_test.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2009. All Rights Reserved. +%% Copyright Ericsson AB 1998-2018. 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% @@ -35,9 +36,9 @@ -endif. init() -> - spawn(fun() -> start(logger) end), + spawn(fun() -> start(wlr_logger) end), spawn(fun() -> start2(wlt) end), - wait_registered(logger), + wait_registered(wlr_logger), wait_registered(wlt), ok. @@ -51,9 +52,9 @@ wait_registered(Name) -> end. stop() -> - catch logger ! exit, + catch wlr_logger ! exit, catch wlt ! exit, - wait_unregistered(logger), + wait_unregistered(wlr_logger), wait_unregistered(wlt), ok. @@ -81,47 +82,47 @@ loop() -> {open, Pid, Name, File} -> R = disk_log:open([{name, Name}, {type, wrap}, {file, File}, {size, {?fsize, ?fno}}]), - ?format("logger: open ~p -> ~p~n", [Name, R]), + ?format("wlr_logger: open ~p -> ~p~n", [Name, R]), Pid ! R, loop(); {open_ext, Pid, Name, File} -> R = disk_log:open([{name, Name}, {type, wrap}, {file, File}, {format, external}, {size, {?fsize, ?fno}}]), - ?format("logger: open ~p -> ~p~n", [Name, R]), + ?format("wlr_logger: open ~p -> ~p~n", [Name, R]), Pid ! R, loop(); {close, Pid, Name} -> R = disk_log:close(Name), - ?format("logger: close ~p -> ~p~n", [Name, R]), + ?format("wlr_logger: close ~p -> ~p~n", [Name, R]), Pid ! R, loop(); {sync, Pid, Name} -> R = disk_log:sync(Name), - ?format("logger: sync ~p -> ~p~n", [Name, R]), + ?format("wlr_logger: sync ~p -> ~p~n", [Name, R]), Pid ! R, loop(); {log_terms, Pid, Name, Terms} -> R = disk_log:log_terms(Name, Terms), - ?format("logger: log_terms ~p -> ~p~n", [Name, R]), + ?format("wlr_logger: log_terms ~p -> ~p~n", [Name, R]), Pid ! R, loop(); {blog_terms, Pid, Name, Terms} -> R = disk_log:blog_terms(Name, Terms), - ?format("logger: blog_terms ~p -> ~p~n", [Name, R]), + ?format("wlr_logger: blog_terms ~p -> ~p~n", [Name, R]), Pid ! R, loop(); exit -> - ?format("Stopping logger~n", []), + ?format("Stopping wlr_logger~n", []), exit(normal); _Else -> - ?format("logger: ignored: ~p~n", [_Else]), + ?format("wlr_logger: ignored: ~p~n", [_Else]), loop() end. diff --git a/lib/kernel/test/zlib_SUITE.erl b/lib/kernel/test/zlib_SUITE.erl index 3be6f39d95..52ae1b3ae6 100644 --- a/lib/kernel/test/zlib_SUITE.erl +++ b/lib/kernel/test/zlib_SUITE.erl @@ -1,78 +1,76 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2013. All Rights Reserved. +%% Copyright Ericsson AB 2005-2018. 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% -module(zlib_SUITE). --include_lib("test_server/include/test_server.hrl"). - --compile(export_all). - --define(error(Format,Args), - put(test_server_loc,{?MODULE,?LINE}), - error(Format,Args,?MODULE,?LINE)). - -%% Learn erts team how to really write tests ;-) --define(m(ExpectedRes,Expr), - fun() -> - ACtual1 = (catch (Expr)), - try case ACtual1 of - ExpectedRes -> ACtual1 - end - catch - error:{case_clause,ACtuAl} -> - ?error("Not Matching Actual result was:~n ~p ~n", - [ACtuAl]), - ACtuAl - end - end()). - --define(BARG, {'EXIT',{badarg,[{zlib,_,_,_}|_]}}). --define(DATA_ERROR, {'EXIT',{data_error,[{zlib,_,_,_}|_]}}). - -init_per_testcase(_Func, Config) -> - Dog = test_server:timetrap(test_server:seconds(60)), - [{watchdog, Dog}|Config]. -end_per_testcase(_Func, Config) -> - Dog = ?config(watchdog, Config), - test_server:timetrap_cancel(Dog). - -error(Format, Args, File, Line) -> - io:format("~p:~p: ERROR: " ++ Format, [File,Line|Args]), - group_leader() ! {failed, File, Line}. - -%% Hopefully I don't need this to get it to work with the testserver.. -%% Fail = #'REASON'{file = filename:basename(File), -%% line = Line, -%% desc = Args}, -%% case global:whereis_name(mnesia_test_case_sup) of -%% undefined -> -%% ignore; -%% Pid -> -%% Pid ! Fail -%% %% global:send(mnesia_test_case_sup, Fail), -%% end, -%% log("<>ERROR<>~n" ++ Format, Args, File, Line). - -suite() -> [{ct_hooks,[ts_install_cth]}]. +-include_lib("common_test/include/ct.hrl"). +-include_lib("common_test/include/ct_event.hrl"). + +-export([suite/0, all/0, groups/0]). + +%% API group +-export([api_open_close/1]). +-export([api_deflateInit/1, api_deflateSetDictionary/1, api_deflateReset/1, + api_deflateParams/1, api_deflate/1, api_deflateEnd/1]). +-export([api_inflateInit/1, api_inflateReset/1, api_inflate2/1, api_inflate3/1, + api_inflateChunk/1, api_safeInflate/1, api_inflateEnd/1]). +-export([api_inflateSetDictionary/1, api_inflateGetDictionary/1]). +-export([api_crc32/1, api_adler32/1]). +-export([api_un_compress/1, api_un_zip/1, api_g_un_zip/1]). + +%% Examples group +-export([intro/1]). + +%% Usage group +-export([zip_usage/1, gz_usage/1, gz_usage2/1, compress_usage/1, + dictionary_usage/1, large_deflate/1, crc/1, adler/1, + only_allow_owner/1, sub_heap_binaries/1]). + +%% Bench group +-export([inflate_bench_zeroed/1, inflate_bench_rand/1, + deflate_bench_zeroed/1, deflate_bench_rand/1, + chunk_bench_zeroed/1, chunk_bench_rand/1]). + +%% Others +-export([smp/1, otp_9981/1, otp_7359/1]). + +-define(m(Guard, Expression), + fun() -> + Actual = (catch (Expression)), + case Actual of + Guard -> Actual; + _Other -> + ct:fail("Failed to match ~p, actual result was ~p", + [??Guard, Actual]) + end + end()). + +-define(EXIT(Reason), {'EXIT',{Reason,[{_,_,_,_}|_]}}). + +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap,{minutes,1}}]. all() -> - [{group, api}, {group, examples}, {group, func}, smp, + [{group, api}, {group, examples}, {group, func}, + {group, bench}, smp, otp_9981, otp_7359]. @@ -81,151 +79,184 @@ groups() -> [api_open_close, api_deflateInit, api_deflateSetDictionary, api_deflateReset, api_deflateParams, api_deflate, api_deflateEnd, - api_inflateInit, api_inflateSetDictionary, - api_inflateSync, api_inflateReset, api_inflate, - api_inflateEnd, api_setBufsz, api_getBufsz, api_crc32, - api_adler32, api_getQSize, api_un_compress, api_un_zip, + api_inflateInit, api_inflateSetDictionary, api_inflateGetDictionary, + api_inflateReset, api_inflate2, api_inflate3, api_inflateChunk, + api_safeInflate, api_inflateEnd, api_crc32, + api_adler32, api_un_compress, api_un_zip, api_g_un_zip]}, {examples, [], [intro]}, {func, [], [zip_usage, gz_usage, gz_usage2, compress_usage, - dictionary_usage, large_deflate, crc, adler]}]. - -init_per_suite(Config) -> - Config. - -end_per_suite(_Config) -> - ok. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - - - -api_open_close(doc) -> "Test open/0 and close/1"; -api_open_close(suite) -> []; + dictionary_usage, large_deflate, crc, adler, + only_allow_owner, sub_heap_binaries]}, + {bench, + [inflate_bench_zeroed, inflate_bench_rand, + deflate_bench_zeroed, deflate_bench_rand, + chunk_bench_zeroed, chunk_bench_rand]}]. + +%% Test open/0 and close/1. api_open_close(Config) when is_list(Config) -> - ?line Fd1 = zlib:open(), - ?line Fd2 = zlib:open(), + Fd1 = zlib:open(), + Fd2 = zlib:open(), ?m(false,Fd1 == Fd2), ?m(ok,zlib:close(Fd1)), - ?m(?BARG, zlib:close(Fd1)), + ?m(?EXIT(not_initialized), zlib:close(Fd1)), ?m(ok,zlib:close(Fd2)), - + %% Make sure that we don't get any EXIT messages if trap_exit is enabled. - ?line process_flag(trap_exit, true), - ?line Fd3 = zlib:open(), + process_flag(trap_exit, true), + Fd3 = zlib:open(), ?m(ok,zlib:close(Fd3)), receive - Any -> ?line ?t:fail({unexpected_message,Any}) + Any -> ct:fail({unexpected_message,Any}) after 10 -> ok end. -api_deflateInit(doc) -> "Test deflateInit/2 and /6"; -api_deflateInit(suite) -> []; +%% Test deflateInit/2 and /6. api_deflateInit(Config) when is_list(Config) -> - ?line Z1 = zlib:open(), - ?m(?BARG, zlib:deflateInit(gurka, none)), - ?m(?BARG, zlib:deflateInit(gurka, gurka)), - ?m(?BARG, zlib:deflateInit(Z1, gurka)), + Z1 = zlib:open(), + + ?m(?EXIT(badarg), zlib:deflateInit(gurka, none)), + + ?m(?EXIT(bad_compression_level), zlib:deflateInit(gurka, gurka)), + ?m(?EXIT(bad_compression_level), zlib:deflateInit(Z1, gurka)), Levels = [none, default, best_speed, best_compression] ++ lists:seq(0,9), lists:foreach(fun(Level) -> - ?line Z = zlib:open(), + Z = zlib:open(), ?m(ok, zlib:deflateInit(Z, Level)), ?m(ok,zlib:close(Z)) end, Levels), %% /6 - ?m(?BARG, zlib:deflateInit(Z1,gurka,deflated,-15,8,default)), - - ?m(?BARG, zlib:deflateInit(Z1,default,undefined,-15,8,default)), - - ?m(?BARG, zlib:deflateInit(Z1,default,deflated,48,8,default)), - ?m(?BARG, zlib:deflateInit(Z1,default,deflated,-20,8,default)), - ?m(?BARG, zlib:deflateInit(Z1,default,deflated,-7,8,default)), - ?m(?BARG, zlib:deflateInit(Z1,default,deflated,7,8,default)), - ?m(?BARG, zlib:deflateInit(Z1,default,deflated,-8,8,default)), - ?m(?BARG, zlib:deflateInit(Z1,default,deflated,8,8,default)), - - ?m(?BARG, zlib:deflateInit(Z1,default,deflated,-15,0,default)), - ?m(?BARG, zlib:deflateInit(Z1,default,deflated,-15,10,default)), - - ?m(?BARG, zlib:deflateInit(Z1,default,deflated,-15,8,0)), - ?m(?BARG, zlib:deflateInit(Z1,default,deflated,-15,8,undefined)), - + ?m(?EXIT(bad_compression_level), + zlib:deflateInit(Z1,gurka,deflated,-15,8,default)), + + ?m(?EXIT(bad_compression_method), + zlib:deflateInit(Z1,default,undefined,-15,8,default)), + + ?m(?EXIT(bad_compression_strategy), + zlib:deflateInit(Z1,default,deflated,-15,8,0)), + ?m(?EXIT(bad_compression_strategy), + zlib:deflateInit(Z1,default,deflated,-15,8,undefined)), + + ?m(?EXIT(bad_windowbits), + zlib:deflateInit(Z1,default,deflated,48,8,default)), + ?m(?EXIT(bad_windowbits), + zlib:deflateInit(Z1,default,deflated,-20,8,default)), + ?m(?EXIT(bad_windowbits), + zlib:deflateInit(Z1,default,deflated,-7,8,default)), + ?m(?EXIT(bad_windowbits), + zlib:deflateInit(Z1,default,deflated,7,8,default)), + + ?m(?EXIT(bad_memlevel), + zlib:deflateInit(Z1,default,deflated,-15,0,default)), + ?m(?EXIT(bad_memlevel), + zlib:deflateInit(Z1,default,deflated,-15,10,default)), + lists:foreach(fun(Level) -> - ?line Z = zlib:open(), + Z = zlib:open(), ?m(ok, zlib:deflateInit(Z, Level, deflated, -15, 8, default)), ?m(ok,zlib:close(Z)) end, Levels), - + lists:foreach(fun(Wbits) -> - ?line Z11 = zlib:open(), + Z11 = zlib:open(), ?m(ok, zlib:deflateInit(Z11,best_compression,deflated, Wbits,8,default)), - ?line Z12 = zlib:open(), + Z12 = zlib:open(), ?m(ok, zlib:deflateInit(Z12,default,deflated,-Wbits,8,default)), ?m(ok,zlib:close(Z11)), ?m(ok,zlib:close(Z12)) end, lists:seq(9, 15)), - + lists:foreach(fun(MemLevel) -> - ?line Z = zlib:open(), + Z = zlib:open(), ?m(ok, zlib:deflateInit(Z,default,deflated,-15, MemLevel,default)), ?m(ok,zlib:close(Z)) end, lists:seq(1,8)), - + Strategies = [filtered,huffman_only,rle,default], lists:foreach(fun(Strategy) -> - ?line Z = zlib:open(), + Z = zlib:open(), ?m(ok, zlib:deflateInit(Z,best_speed,deflated,-15,8,Strategy)), ?m(ok,zlib:close(Z)) end, Strategies), ?m(ok, zlib:deflateInit(Z1,default,deflated,-15,8,default)), - ?m({'EXIT',_}, zlib:deflateInit(Z1,none,deflated,-15,8,default)), %% ?? + + %% Let it crash for any reason; we don't care about the order in which the + %% parameters are checked. + ?m(?EXIT(_), zlib:deflateInit(Z1,none,deflated,-15,8,default)), + ?m(ok, zlib:close(Z1)). -api_deflateSetDictionary(doc) -> "Test deflateSetDictionary"; -api_deflateSetDictionary(suite) -> []; +%% Test deflateSetDictionary. api_deflateSetDictionary(Config) when is_list(Config) -> - ?line Z1 = zlib:open(), + Z1 = zlib:open(), ?m(ok, zlib:deflateInit(Z1, default)), ?m(Id when is_integer(Id), zlib:deflateSetDictionary(Z1, <<1,1,2,3,4,5,1>>)), ?m(Id when is_integer(Id), zlib:deflateSetDictionary(Z1, [1,1,2,3,4,5,1])), - ?m(?BARG, zlib:deflateSetDictionary(Z1, gurka)), - ?m(?BARG, zlib:deflateSetDictionary(Z1, 128)), - ?m(_, zlib:deflate(Z1, <<1,1,1,1,1,1,1,1,1>>, none)), - ?m({'EXIT',{stream_error,_}},zlib:deflateSetDictionary(Z1,<<1,1,2,3,4,5,1>>)), + ?m(?EXIT(badarg), zlib:deflateSetDictionary(Z1, gurka)), + ?m(?EXIT(badarg), zlib:deflateSetDictionary(Z1, 128)), + ?m(L when is_list(L), zlib:deflate(Z1, <<1,1,1,1,1,1,1,1,1>>, none)), + ?m(?EXIT(stream_error), zlib:deflateSetDictionary(Z1,<<1,1,2,3,4,5,1>>)), ?m(ok, zlib:close(Z1)). -api_deflateReset(doc) -> "Test deflateReset"; -api_deflateReset(suite) -> []; +%% Test deflateReset. api_deflateReset(Config) when is_list(Config) -> - ?line Z1 = zlib:open(), + Z1 = zlib:open(), ?m(ok, zlib:deflateInit(Z1, default)), - ?m(_, zlib:deflate(Z1, <<1,1,1,1,1,1,1,1,1>>, none)), + ?m(L when is_list(L), zlib:deflate(Z1, <<1,1,1,1,1,1,1,1,1>>, none)), ?m(ok, zlib:deflateReset(Z1)), ?m(ok, zlib:deflateReset(Z1)), %% FIXME how do I make this go wrong?? ?m(ok, zlib:close(Z1)). -api_deflateParams(doc) -> "Test deflateParams"; -api_deflateParams(suite) -> []; +%% Test deflateParams. api_deflateParams(Config) when is_list(Config) -> - ?line Z1 = zlib:open(), + Levels = [none, default, best_speed, best_compression] ++ lists:seq(0, 9), + Strategies = [filtered, huffman_only, rle, default], + + Z1 = zlib:open(), ?m(ok, zlib:deflateInit(Z1, default)), - ?m(_, zlib:deflate(Z1, <<1,1,1,1,1,1,1,1,1>>, none)), - ?m(ok, zlib:deflateParams(Z1, best_compression, huffman_only)), - ?m(_, zlib:deflate(Z1, <<1,1,1,1,1,1,1,1,1>>, sync)), - ?m(ok, zlib:close(Z1)). -api_deflate(doc) -> "Test deflate"; -api_deflate(suite) -> []; + ApiTest = + fun(Level, Strategy) -> + ?m(ok, zlib:deflateParams(Z1, Level, Strategy)), + ?m(ok, zlib:deflateReset(Z1)) + end, + + [ ApiTest(Level, Strategy) || Level <- Levels, Strategy <- Strategies ], + + ?m(ok, zlib:close(Z1)), + + FlushTest = + fun FlushTest(Size, Level, Strategy) -> + Z = zlib:open(), + ok = zlib:deflateInit(Z, default), + Data = gen_determ_rand_bytes(Size), + case zlib:deflate(Z, Data, none) of + [<<120, 156>>] -> + %% All data is present in the internal zlib state, and will + %% be flushed on deflateParams. + + ok = zlib:deflateParams(Z, Level, Strategy), + Compressed = [<<120, 156>>, zlib:deflate(Z, <<>>, finish)], + Data = zlib:uncompress(Compressed), + zlib:close(Z), + + FlushTest(Size + (1 bsl 10), Level, Strategy); + _Other -> + ok + end + end, + + [ FlushTest(1, Level, Strategy) || Level <- Levels, Strategy <- Strategies ], + + ok. + +%% Test deflate. api_deflate(Config) when is_list(Config) -> - ?line Z1 = zlib:open(), + Z1 = zlib:open(), ?m(ok, zlib:deflateInit(Z1, default)), ?m([B] when is_binary(B), zlib:deflate(Z1, <<1,1,1,1,1,1,1,1,1>>, finish)), ?m(ok, zlib:deflateReset(Z1)), @@ -236,291 +267,435 @@ api_deflate(Config) when is_list(Config) -> ?m(B when is_list(B), zlib:deflate(Z1, <<1,1,1,1,1,1,1,1,1>>, sync)), ?m(B when is_list(B), zlib:deflate(Z1, <<1,1,1,1,1,1,1,1,1>>, full)), ?m(B when is_list(B), zlib:deflate(Z1, <<>>, finish)), - - ?m(?BARG, zlib:deflate(gurka, <<1,1,1,1,1,1,1,1,1>>, full)), - ?m(?BARG, zlib:deflate(Z1, <<1,1,1,1,1,1,1,1,1>>, asdj)), - ?m(?BARG, zlib:deflate(Z1, <<1,1,1,1,1,1,1,1,1>>, 198)), + + ?m(?EXIT(badarg), zlib:deflate(gurka, <<1,1,1,1,1,1,1,1,1>>, full)), + + ?m(?EXIT(bad_flush_mode), zlib:deflate(Z1, <<1,1,1,1,1,1,1,1,1>>, asdj)), + ?m(?EXIT(bad_flush_mode), zlib:deflate(Z1, <<1,1,1,1,1,1,1,1,1>>, 198)), + %% Causes problems ERROR REPORT - ?m(?BARG, zlib:deflate(Z1, [asdj,asd], none)), - + ?m(?EXIT(badarg), zlib:deflate(Z1, [asdj,asd], none)), + ?m(ok, zlib:close(Z1)). -api_deflateEnd(doc) -> "Test deflateEnd"; -api_deflateEnd(suite) -> []; +%% Test deflateEnd. api_deflateEnd(Config) when is_list(Config) -> - ?line Z1 = zlib:open(), + Z1 = zlib:open(), ?m(ok, zlib:deflateInit(Z1, default)), ?m(ok, zlib:deflateEnd(Z1)), - ?m({'EXIT', {einval,_}}, zlib:deflateEnd(Z1)), %% ?? - ?m(?BARG, zlib:deflateEnd(gurka)), + ?m(?EXIT(not_initialized), zlib:deflateEnd(Z1)), + ?m(?EXIT(badarg), zlib:deflateEnd(gurka)), ?m(ok, zlib:deflateInit(Z1, default)), ?m(B when is_list(B), zlib:deflate(Z1, <<"Kilroy was here">>)), - ?m({'EXIT', {data_error,_}}, zlib:deflateEnd(Z1)), + ?m(?EXIT(data_error), zlib:deflateEnd(Z1)), ?m(ok, zlib:deflateInit(Z1, default)), ?m(B when is_list(B), zlib:deflate(Z1, <<"Kilroy was here">>)), ?m(B when is_list(B), zlib:deflate(Z1, <<"Kilroy was here">>, finish)), ?m(ok, zlib:deflateEnd(Z1)), - + ?m(ok, zlib:close(Z1)). -api_inflateInit(doc) -> "Test inflateInit /1 and /2"; -api_inflateInit(suite) -> []; +%% Test inflateInit /1 and /2. api_inflateInit(Config) when is_list(Config) -> - ?line Z1 = zlib:open(), - ?m(?BARG, zlib:inflateInit(gurka)), + Z1 = zlib:open(), + ?m(?EXIT(badarg), zlib:inflateInit(gurka)), ?m(ok, zlib:inflateInit(Z1)), - ?m({'EXIT',{einval,_}}, zlib:inflateInit(Z1, 15)), %% ?? + ?m(?EXIT(already_initialized), zlib:inflateInit(Z1, 15)), lists:foreach(fun(Wbits) -> - ?line Z11 = zlib:open(), + Z11 = zlib:open(), ?m(ok, zlib:inflateInit(Z11,Wbits)), - ?line Z12 = zlib:open(), + Z12 = zlib:open(), ?m(ok, zlib:inflateInit(Z12,-Wbits)), ?m(ok,zlib:close(Z11)), ?m(ok,zlib:close(Z12)) - end, lists:seq(9,15)), - ?m(?BARG, zlib:inflateInit(gurka, -15)), - ?m(?BARG, zlib:inflateInit(Z1, 7)), - ?m(?BARG, zlib:inflateInit(Z1, -7)), - ?m(?BARG, zlib:inflateInit(Z1, 48)), - ?m(?BARG, zlib:inflateInit(Z1, -16)), + end, lists:seq(8,15)), + ?m(?EXIT(badarg), zlib:inflateInit(gurka, -15)), + ?m(?EXIT(bad_windowbits), zlib:inflateInit(Z1, 7)), + ?m(?EXIT(bad_windowbits), zlib:inflateInit(Z1, -7)), + ?m(?EXIT(bad_windowbits), zlib:inflateInit(Z1, 48)), + ?m(?EXIT(bad_windowbits), zlib:inflateInit(Z1, -16)), ?m(ok, zlib:close(Z1)). -api_inflateSetDictionary(doc) -> "Test inflateSetDictionary"; -api_inflateSetDictionary(suite) -> []; +%% Test inflateSetDictionary. api_inflateSetDictionary(Config) when is_list(Config) -> - ?line Z1 = zlib:open(), + Z1 = zlib:open(), ?m(ok, zlib:inflateInit(Z1)), - ?m(?BARG, zlib:inflateSetDictionary(gurka,<<1,1,1,1,1>>)), - ?m(?BARG, zlib:inflateSetDictionary(Z1,102)), - ?m(?BARG, zlib:inflateSetDictionary(Z1,gurka)), + ?m(?EXIT(badarg), zlib:inflateSetDictionary(gurka,<<1,1,1,1,1>>)), + ?m(?EXIT(badarg), zlib:inflateSetDictionary(Z1,102)), + ?m(?EXIT(badarg), zlib:inflateSetDictionary(Z1,gurka)), Dict = <<1,1,1,1,1>>, - ?m({'EXIT',{stream_error,_}}, zlib:inflateSetDictionary(Z1,Dict)), + ?m(?EXIT(stream_error), zlib:inflateSetDictionary(Z1,Dict)), ?m(ok, zlib:close(Z1)). -api_inflateSync(doc) -> "Test inflateSync"; -api_inflateSync(suite) -> []; -api_inflateSync(Config) when is_list(Config) -> - {skip,"inflateSync/1 sucks"}. -%% ?line Z1 = zlib:open(), -%% ?m(ok, zlib:deflateInit(Z1)), -%% ?line B1list0 = zlib:deflate(Z1, "gurkan gurra ger galna tunnor", full), -%% ?line B2 = zlib:deflate(Z1, "grodan boll", finish), -%% io:format("~p\n", [B1list0]), -%% io:format("~p\n", [B2]), -%% ?m(ok, zlib:deflateEnd(Z1)), -%% ?line B1 = clobber(14, list_to_binary(B1list0)), -%% ?line Compressed = list_to_binary([B1,B2]), -%% ?line io:format("~p\n", [Compressed]), - -%% ?m(ok, zlib:inflateInit(Z1)), -%% ?m(?BARG, zlib:inflateSync(gurka)), -%% ?m({'EXIT',{data_error,_}}, zlib:inflate(Z1, Compressed)), -%% ?m(ok, zlib:inflateSync(Z1)), -%% ?line Ubs = zlib:inflate(Z1, []), -%% ?line <<"grodan boll">> = list_to_binary(Ubs), -%% ?m(ok, zlib:close(Z1)). - -clobber(N, Bin) when is_binary(Bin) -> - T = list_to_tuple(binary_to_list(Bin)), - Byte = case element(N, T) of - 255 -> 254; - B -> B+1 - end, - list_to_binary(tuple_to_list(setelement(N, T, Byte))). +%% Test inflateGetDictionary. +api_inflateGetDictionary(Config) when is_list(Config) -> + Z1 = zlib:open(), + zlib:inflateInit(Z1), + IsOperationSupported = + case catch zlib:inflateGetDictionary(Z1) of + ?EXIT(not_supported) -> false; + _ -> true + end, + zlib:close(Z1), + api_inflateGetDictionary_if_supported(IsOperationSupported). + +api_inflateGetDictionary_if_supported(false) -> + {skip, "inflateGetDictionary/1 unsupported in current setup"}; +api_inflateGetDictionary_if_supported(true) -> + % Compress payload using custom dictionary + Z1 = zlib:open(), + ?m(ok, zlib:deflateInit(Z1)), + Dict = <<"foobar barfoo foo bar far boo">>, + Checksum = zlib:deflateSetDictionary(Z1, Dict), + Payload = <<"foobarbarbar">>, + Compressed = zlib:deflate(Z1, Payload, finish), + ?m(ok, zlib:close(Z1)), + + % Decompress and test dictionary extraction with inflate/2 + Z2 = zlib:open(), + ?m(ok, zlib:inflateInit(Z2)), + ?m(<<>>, iolist_to_binary(zlib:inflateGetDictionary(Z2))), + ?m(?EXIT(stream_error), zlib:inflateSetDictionary(Z2, Dict)), + ?m(?EXIT({need_dictionary,Checksum}), zlib:inflate(Z2, Compressed)), + ?m(ok, zlib:inflateSetDictionary(Z2, Dict)), + ?m(Dict, iolist_to_binary(zlib:inflateGetDictionary(Z2))), + Payload = iolist_to_binary(zlib:inflate(Z2, [])), + ?m(ok, zlib:close(Z2)), + ?m(?EXIT(not_initialized), zlib:inflateSetDictionary(Z2, Dict)), + + %% ... And do the same for inflate/3 + Z3 = zlib:open(), + ?m(ok, zlib:inflateInit(Z3)), + ?m(<<>>, iolist_to_binary(zlib:inflateGetDictionary(Z3))), + ?m(?EXIT(stream_error), zlib:inflateSetDictionary(Z3, Dict)), + + {need_dictionary, Checksum, _Output = []} = + zlib:inflate(Z3, Compressed, [{exception_on_need_dict, false}]), + + ?m(ok, zlib:inflateSetDictionary(Z3, Dict)), + ?m(Dict, iolist_to_binary(zlib:inflateGetDictionary(Z3))), -api_inflateReset(doc) -> "Test inflateReset"; -api_inflateReset(suite) -> []; + Payload = iolist_to_binary( + zlib:inflate(Z3, [], [{exception_on_need_dict, false}])), + + ?m(ok, zlib:close(Z3)), + ?m(?EXIT(not_initialized), zlib:inflateSetDictionary(Z3, Dict)), + + ok. + +%% Test inflateReset. api_inflateReset(Config) when is_list(Config) -> - ?line Z1 = zlib:open(), + Z1 = zlib:open(), ?m(ok, zlib:inflateInit(Z1)), - ?m(?BARG, zlib:inflateReset(gurka)), + ?m(?EXIT(badarg), zlib:inflateReset(gurka)), ?m(ok, zlib:inflateReset(Z1)), ?m(ok, zlib:close(Z1)). -api_inflate(doc) -> "Test inflate"; -api_inflate(suite) -> []; -api_inflate(Config) when is_list(Config) -> +%% Test inflate/2 +api_inflate2(Config) when is_list(Config) -> Data = [<<1,2,2,3,3,3,4,4,4,4>>], - ?line Compressed = zlib:compress(Data), - ?line Z1 = zlib:open(), + Compressed = zlib:compress(Data), + Z1 = zlib:open(), ?m(ok, zlib:inflateInit(Z1)), ?m([], zlib:inflate(Z1, <<>>)), ?m(Data, zlib:inflate(Z1, Compressed)), ?m(ok, zlib:inflateEnd(Z1)), ?m(ok, zlib:inflateInit(Z1)), ?m(Data, zlib:inflate(Z1, Compressed)), - ?m(?BARG, zlib:inflate(gurka, Compressed)), - ?m(?BARG, zlib:inflate(Z1, 4384)), - ?m(?BARG, zlib:inflate(Z1, [atom_list])), + ?m(?EXIT(badarg), zlib:inflate(gurka, Compressed)), + ?m(?EXIT(badarg), zlib:inflate(Z1, 4384)), + ?m(?EXIT(badarg), zlib:inflate(Z1, [atom_list])), ?m(ok, zlib:inflateEnd(Z1)), ?m(ok, zlib:inflateInit(Z1)), - ?m({'EXIT',{data_error,_}}, zlib:inflate(Z1, <<2,1,2,1,2>>)), + ?m(?EXIT(data_error), zlib:inflate(Z1, <<2,1,2,1,2>>)), ?m(ok, zlib:close(Z1)). -api_inflateEnd(doc) -> "Test inflateEnd"; -api_inflateEnd(suite) -> []; -api_inflateEnd(Config) when is_list(Config) -> - ?line Z1 = zlib:open(), - ?m({'EXIT',{einval,_}}, zlib:inflateEnd(Z1)), +%% Test inflate/3; same as inflate/2 but with the default options inverted. +api_inflate3(Config) when is_list(Config) -> + Data = [<<1,2,2,3,3,3,4,4,4,4>>], + Options = [{exception_on_need_dict, false}], + Compressed = zlib:compress(Data), + Z1 = zlib:open(), ?m(ok, zlib:inflateInit(Z1)), - ?m(?BARG, zlib:inflateEnd(gurka)), - ?m({'EXIT',{data_error,_}}, zlib:inflateEnd(Z1)), - ?m({'EXIT',{einval,_}}, zlib:inflateEnd(Z1)), + ?m([], zlib:inflate(Z1, <<>>, Options)), + ?m(Data, zlib:inflate(Z1, Compressed)), + ?m(ok, zlib:inflateEnd(Z1)), ?m(ok, zlib:inflateInit(Z1)), - ?m(B when is_list(B), zlib:inflate(Z1, zlib:compress("abc"))), + ?m(Data, zlib:inflate(Z1, Compressed, Options)), + ?m(?EXIT(badarg), zlib:inflate(gurka, Compressed, Options)), + ?m(?EXIT(badarg), zlib:inflate(Z1, 4384, Options)), + ?m(?EXIT(badarg), zlib:inflate(Z1, [atom_list], Options)), ?m(ok, zlib:inflateEnd(Z1)), + ?m(ok, zlib:inflateInit(Z1)), + ?m(?EXIT(data_error), zlib:inflate(Z1, <<2,1,2,1,2>>, Options)), ?m(ok, zlib:close(Z1)). -api_getBufsz(doc) -> "Test getBufsz"; -api_getBufsz(suite) -> []; -api_getBufsz(Config) when is_list(Config) -> - ?line Z1 = zlib:open(), - ?m(Val when is_integer(Val), zlib:getBufSize(Z1)), - ?m(?BARG, zlib:getBufSize(gurka)), +%% Test inflateChunk. +api_inflateChunk(Config) when is_list(Config) -> + ChunkSize = 1024, + Data = << <<(I rem 150)>> || I <- lists:seq(1, 3 * ChunkSize) >>, + Part1 = binary:part(Data, 0, ChunkSize), + Part2 = binary:part(Data, ChunkSize, ChunkSize), + Part3 = binary:part(Data, ChunkSize * 2, ChunkSize), + + Compressed = zlib:compress(Data), + Z1 = zlib:open(), + + zlib:setBufSize(Z1, ChunkSize), + + ?m(ok, zlib:inflateInit(Z1)), + + 0 = iolist_size(zlib:inflateChunk(Z1, <<>>)), + + {more, Part1AsIOList} = zlib:inflateChunk(Z1, Compressed), + {more, Part2AsIOList} = zlib:inflateChunk(Z1), + {more, Part3AsIOList} = zlib:inflateChunk(Z1), + + [] = zlib:inflateChunk(Z1), + [] = zlib:inflateChunk(Z1), + [] = zlib:inflateChunk(Z1), + + ?m(Part1, iolist_to_binary(Part1AsIOList)), + ?m(Part2, iolist_to_binary(Part2AsIOList)), + ?m(Part3, iolist_to_binary(Part3AsIOList)), + + ?m(ok, zlib:inflateEnd(Z1)), + ?m(ok, zlib:inflateInit(Z1)), + + ?m({more, Part1AsIOList}, zlib:inflateChunk(Z1, Compressed)), + + ?m(ok, zlib:inflateReset(Z1)), + + zlib:setBufSize(Z1, byte_size(Data) + 1), + + DataAsIOList = zlib:inflateChunk(Z1, Compressed), + ?m(Data, iolist_to_binary(DataAsIOList)), + + ?m(ok, zlib:inflateEnd(Z1)), + ?m(ok, zlib:inflateInit(Z1)), + + ?m(?EXIT(badarg), zlib:inflateChunk(gurka, Compressed)), + ?m(?EXIT(badarg), zlib:inflateChunk(Z1, 4384)), + + ?m(?EXIT(data_error), zlib:inflateEnd(Z1)), + ?m(ok, zlib:close(Z1)). -api_setBufsz(doc) -> "Test setBufsz"; -api_setBufsz(suite) -> []; -api_setBufsz(Config) when is_list(Config) -> - ?line Z1 = zlib:open(), - ?m(?BARG, zlib:setBufSize(Z1, gurka)), - ?m(?BARG, zlib:setBufSize(gurka, 1232330)), - Sz = ?m( Val when is_integer(Val), zlib:getBufSize(Z1)), - ?m(ok, zlib:setBufSize(Z1, Sz*2)), - DSz = Sz*2, - ?m(DSz, zlib:getBufSize(Z1)), +%% Test safeInflate as a mirror of inflateChunk, but ignore the stuff about +%% exact chunk sizes. +api_safeInflate(Config) when is_list(Config) -> + Data = << <<(I rem 150)>> || I <- lists:seq(1, 20 bsl 10) >>, + Compressed = zlib:compress(Data), + Z1 = zlib:open(), + + ?m(ok, zlib:inflateInit(Z1)), + + SafeInflateLoop = + fun + Loop({continue, Chunk}, Output) -> + Loop(zlib:safeInflate(Z1, []), [Output, Chunk]); + Loop({finished, Chunk}, Output) -> + [Output, Chunk] + end, + + Decompressed = SafeInflateLoop(zlib:safeInflate(Z1, Compressed), []), + Data = iolist_to_binary(Decompressed), + + ?m(ok, zlib:inflateEnd(Z1)), + ?m(ok, zlib:inflateInit(Z1)), + + {continue, Partial} = zlib:safeInflate(Z1, Compressed), + PBin = iolist_to_binary(Partial), + PSize = byte_size(PBin), + <<PBin:PSize/binary, Rest/binary>> = Data, + + ?m(ok, zlib:inflateReset(Z1)), + + {continue, Partial} = zlib:safeInflate(Z1, Compressed), + PBin = iolist_to_binary(Partial), + PSize = byte_size(PBin), + <<PBin:PSize/binary, Rest/binary>> = Data, + + ?m(ok, zlib:inflateReset(Z1)), + + SafeInflateLoop(zlib:safeInflate(Z1, Compressed), []), + + ?m({finished, []}, zlib:safeInflate(Z1, Compressed)), + ?m({finished, []}, zlib:safeInflate(Z1, Compressed)), + + ?m(ok, zlib:inflateReset(Z1)), + ?m(?EXIT(badarg), zlib:safeInflate(gurka, Compressed)), + ?m(?EXIT(badarg), zlib:safeInflate(Z1, 4384)), + ?m(?EXIT(data_error), zlib:inflateEnd(Z1)), ?m(ok, zlib:close(Z1)). -%%% Debug function ?? -api_getQSize(doc) -> "Test getQSize"; -api_getQSize(suite) -> []; -api_getQSize(Config) when is_list(Config) -> - ?line Z1 = zlib:open(), - Q = ?m(Val when is_integer(Val), zlib:getQSize(Z1)), - io:format("QSize ~p ~n", [Q]), - ?m(?BARG, zlib:getQSize(gurka)), +%% Test inflateEnd. +api_inflateEnd(Config) when is_list(Config) -> + Z1 = zlib:open(), + ?m(?EXIT(not_initialized), zlib:inflateEnd(Z1)), + ?m(ok, zlib:inflateInit(Z1)), + ?m(?EXIT(badarg), zlib:inflateEnd(gurka)), + ?m(?EXIT(data_error), zlib:inflateEnd(Z1)), + ?m(?EXIT(not_initialized), zlib:inflateEnd(Z1)), + ?m(ok, zlib:inflateInit(Z1)), + ?m(B when is_list(B), zlib:inflate(Z1, zlib:compress("abc"))), + ?m(ok, zlib:inflateEnd(Z1)), ?m(ok, zlib:close(Z1)). -api_crc32(doc) -> "Test crc32"; -api_crc32(suite) -> []; +%% Test crc32. api_crc32(Config) when is_list(Config) -> - ?line Z1 = zlib:open(), + Z1 = zlib:open(), ?m(ok, zlib:deflateInit(Z1,best_speed,deflated,-15,8,default)), Bin = <<1,1,1,1,1,1,1,1,1>>, - Compressed1 = ?m(_, zlib:deflate(Z1, Bin, none)), - Compressed2 = ?m(_, zlib:deflate(Z1, <<>>, finish)), + Compressed1 = ?m(L when is_list(L), zlib:deflate(Z1, Bin, none)), + Compressed2 = ?m(L when is_list(L), zlib:deflate(Z1, <<>>, finish)), Compressed = list_to_binary(Compressed1 ++ Compressed2), - CRC1 = ?m( CRC1 when is_integer(CRC1), zlib:crc32(Z1)), + CRC1 = ?m( CRC1 when is_integer(CRC1), zlib:crc32(Z1)), ?m(CRC1 when is_integer(CRC1), zlib:crc32(Z1,Bin)), ?m(CRC1 when is_integer(CRC1), zlib:crc32(Z1,binary_to_list(Bin))), ?m(CRC2 when is_integer(CRC2), zlib:crc32(Z1,Compressed)), CRC2 = ?m(CRC2 when is_integer(CRC2), zlib:crc32(Z1,0,Compressed)), ?m(CRC3 when CRC2 /= CRC3, zlib:crc32(Z1,234,Compressed)), - ?m(?BARG, zlib:crc32(gurka)), - ?m(?BARG, zlib:crc32(Z1, not_a_binary)), - ?m(?BARG, zlib:crc32(gurka, <<1,1,2,4,4>>)), - ?m(?BARG, zlib:crc32(Z1, 2298929, not_a_binary)), - ?m(?BARG, zlib:crc32(Z1, not_an_int, <<123,123,123,35,231>>)), - ?m(?BARG, zlib:crc32_combine(Z1, not_an_int, 123123, 123)), - ?m(?BARG, zlib:crc32_combine(Z1, noint, 123123, 123)), - ?m(?BARG, zlib:crc32_combine(Z1, 123123, noint, 123)), - ?m(?BARG, zlib:crc32_combine(Z1, 123123, 123, noint)), + ?m(?EXIT(badarg), zlib:crc32(gurka)), + ?m(?EXIT(badarg), zlib:crc32(Z1, not_a_binary)), + ?m(?EXIT(badarg), zlib:crc32(gurka, <<1,1,2,4,4>>)), + ?m(?EXIT(badarg), zlib:crc32(Z1, 2298929, not_a_binary)), + ?m(?EXIT(badarg), zlib:crc32(Z1, not_an_int, <<123,123,123,35,231>>)), + ?m(?EXIT(badarg), zlib:crc32_combine(Z1, not_an_int, 123123, 123)), + ?m(?EXIT(badarg), zlib:crc32_combine(Z1, noint, 123123, 123)), + ?m(?EXIT(badarg), zlib:crc32_combine(Z1, 123123, noint, 123)), + ?m(?EXIT(badarg), zlib:crc32_combine(Z1, 123123, 123, noint)), ?m(ok, zlib:deflateEnd(Z1)), ?m(ok, zlib:close(Z1)). -api_adler32(doc) -> "Test adler32"; -api_adler32(suite) -> []; +%% Test adler32. api_adler32(Config) when is_list(Config) -> - ?line Z1 = zlib:open(), + Z1 = zlib:open(), ?m(ok, zlib:deflateInit(Z1,best_speed,deflated,-15,8,default)), Bin = <<1,1,1,1,1,1,1,1,1>>, - Compressed1 = ?m(_, zlib:deflate(Z1, Bin, none)), - Compressed2 = ?m(_, zlib:deflate(Z1, <<>>, finish)), + Compressed1 = ?m(L when is_list(L), zlib:deflate(Z1, Bin, none)), + Compressed2 = ?m(L when is_list(L), zlib:deflate(Z1, <<>>, finish)), Compressed = list_to_binary(Compressed1 ++ Compressed2), ?m(ADLER1 when is_integer(ADLER1), zlib:adler32(Z1,Bin)), ?m(ADLER1 when is_integer(ADLER1), zlib:adler32(Z1,binary_to_list(Bin))), ADLER2 = ?m(ADLER2 when is_integer(ADLER2), zlib:adler32(Z1,Compressed)), ?m(ADLER2 when is_integer(ADLER2), zlib:adler32(Z1,1,Compressed)), ?m(ADLER3 when ADLER2 /= ADLER3, zlib:adler32(Z1,234,Compressed)), - ?m(?BARG, zlib:adler32(Z1, not_a_binary)), - ?m(?BARG, zlib:adler32(gurka, <<1,1,2,4,4>>)), - ?m(?BARG, zlib:adler32(Z1, 2298929, not_a_binary)), - ?m(?BARG, zlib:adler32(Z1, not_an_int, <<123,123,123,35,231>>)), - ?m(?BARG, zlib:adler32_combine(Z1, noint, 123123, 123)), - ?m(?BARG, zlib:adler32_combine(Z1, 123123, noint, 123)), - ?m(?BARG, zlib:adler32_combine(Z1, 123123, 123, noint)), + ?m(?EXIT(badarg), zlib:adler32(Z1, not_a_binary)), + ?m(?EXIT(badarg), zlib:adler32(gurka, <<1,1,2,4,4>>)), + ?m(?EXIT(badarg), zlib:adler32(Z1, 2298929, not_a_binary)), + ?m(?EXIT(badarg), zlib:adler32(Z1, not_an_int, <<123,123,123,35,231>>)), + ?m(?EXIT(badarg), zlib:adler32_combine(Z1, noint, 123123, 123)), + ?m(?EXIT(badarg), zlib:adler32_combine(Z1, 123123, noint, 123)), + ?m(?EXIT(badarg), zlib:adler32_combine(Z1, 123123, 123, noint)), ?m(ok, zlib:deflateEnd(Z1)), ?m(ok, zlib:close(Z1)). -api_un_compress(doc) -> "Test compress"; -api_un_compress(suite) -> []; +%% Test compress. api_un_compress(Config) when is_list(Config) -> - ?m(?BARG,zlib:compress(not_a_binary)), + ?m(?EXIT(badarg),zlib:compress(not_a_binary)), Bin = <<1,11,1,23,45>>, - ?line Comp = zlib:compress(Bin), - ?m(?BARG,zlib:uncompress(not_a_binary)), - ?m({'EXIT',{data_error,_}}, zlib:uncompress(<<171,171,171,171,171>>)), - ?m({'EXIT',{data_error,_}}, zlib:uncompress(<<>>)), - ?m({'EXIT',{data_error,_}}, zlib:uncompress(<<120>>)), - ?m({'EXIT',{data_error,_}}, zlib:uncompress(<<120,156>>)), - ?m({'EXIT',{data_error,_}}, zlib:uncompress(<<120,156,3>>)), - ?m({'EXIT',{data_error,_}}, zlib:uncompress(<<120,156,3,0>>)), - ?m({'EXIT',{data_error,_}}, zlib:uncompress(<<0,156,3,0,0,0,0,1>>)), + Comp = zlib:compress(Bin), + ?m(?EXIT(badarg),zlib:uncompress(not_a_binary)), + ?m(?EXIT(data_error), zlib:uncompress(<<171,171,171,171,171>>)), + ?m(?EXIT(data_error), zlib:uncompress(<<>>)), + ?m(?EXIT(data_error), zlib:uncompress(<<120>>)), + ?m(?EXIT(data_error), zlib:uncompress(<<120,156>>)), + ?m(?EXIT(data_error), zlib:uncompress(<<120,156,3>>)), + ?m(?EXIT(data_error), zlib:uncompress(<<120,156,3,0>>)), + ?m(?EXIT(data_error), zlib:uncompress(<<0,156,3,0,0,0,0,1>>)), ?m(Bin, zlib:uncompress(binary_to_list(Comp))), ?m(Bin, zlib:uncompress(Comp)). -api_un_zip(doc) -> "Test zip"; -api_un_zip(suite) -> []; +%% Test zip. api_un_zip(Config) when is_list(Config) -> - ?m(?BARG,zlib:zip(not_a_binary)), + ?m(?EXIT(badarg),zlib:zip(not_a_binary)), Bin = <<1,11,1,23,45>>, - ?line Comp = zlib:zip(Bin), + Comp = zlib:zip(Bin), ?m(Comp, zlib:zip(binary_to_list(Bin))), - ?m(?BARG,zlib:unzip(not_a_binary)), - ?m({'EXIT',{data_error,_}}, zlib:unzip(<<171,171,171,171,171>>)), - ?m({'EXIT',{data_error,_}}, zlib:unzip(<<>>)), + ?m(?EXIT(badarg),zlib:unzip(not_a_binary)), + ?m(?EXIT(data_error), zlib:unzip(<<171,171,171,171,171>>)), + ?m(?EXIT(data_error), zlib:unzip(<<>>)), ?m(Bin, zlib:unzip(Comp)), ?m(Bin, zlib:unzip(binary_to_list(Comp))), - + %% OTP-6396 - B = <<131,104,19,100,0,13,99,95,99,105,100,95,99,115,103,115,110,95,50,97,1,107,0,4,208,161,246,29,107,0,3,237,166,224,107,0,6,66,240,153,0,2,10,1,0,8,97,116,116,97,99,104,101,100,104,2,100,0,22,117,112,100,97,116,101,95,112,100,112,95,99,111,110,116,101,120,116,95,114,101,113,107,0,114,69,3,12,1,11,97,31,113,150,64,104,132,61,64,104,12,3,197,31,113,150,64,104,132,61,64,104,12,1,11,97,31,115,150,64,104,116,73,64,104,0,0,0,0,0,0,65,149,16,61,65,149,16,61,1,241,33,4,5,0,33,4,4,10,6,10,181,4,10,6,10,181,38,15,99,111,109,109,97,110,100,1,114,45,97,112,110,45,49,3,99,111,109,5,109,110,99,57,57,6,109,99,99,50,52,48,4,103,112,114,115,8,0,104,2,104,2,100,0,8,97,99,116,105,118,97,116,101,104,23,100,0,11,112,100,112,95,99,111,110,116,1,120,116,100,0,7,112,114,105,109,97,114,121,97,1,100,0,9,117,110,100,101,102,105,110,101,100,97,1,97,4,97,4,97,7,100,0,9,117,110,100,101,102,105,110,101,100,100,0,9,117,110,100,101,102,105,110,10100,100,0,9,117,110,100,101,102,105,110,101,100,100,0,5,102,97,108,115,101,100,0,9,117,110,100,101,102,105,110,101,100,100,0,9,117,110,100,101,102,105,110,101,100,100,0,9,117,110,100,101,102,105,1,101,100,97,0,100,0,9,117,110,100,101,102,105,110,101,100,107,0,4,16,0,1,144,107,0,4,61,139,186,181,107,0,4,10,8,201,49,100,0,9,117,110,100,101,102,105,110,101,100,100,0,9,117,110,100,101,102,105,0,101,100,100,0,9,117,110,100,101,102,105,110,101,100,104,2,104,3,98,0,0,7,214,97,11,97,20,104,3,97,17,97,16,97,21,106,108,0,0,0,3,104,2,97,1,104,2,104,3,98,0,0,7,214,97,11,97,20,104,3,97,17,97,167,20,104,2,97,4,104,2,104,3,98,0,0,7,214,97,11,97,20,104,3,97,17,97,16,97,21,104,2,97,10,104,2,104,3,98,0,0,7,214,97,11,97,20,104,3,97,17,97,16,97,26,106,100,0,5,118,101,114,57,57,100,0,9,117,110,0,101,102,105,110,101,100,107,0,2,0,244,107,0,4,10,6,102,195,107,0,4,10,6,102,195,100,0,9,117,110,100,101,102,105,110,101,100,100,0,9,117,110,100,101,102,105,110,101,100,107,0,125,248,143,0,203,25115,157,116,65,185,65,172,55,87,164,88,225,50,203,251,115,157,116,65,185,65,172,55,87,164,88,225,50,0,0,82,153,50,0,200,98,87,148,237,193,185,65,149,167,69,144,14,16,153,50,3,81,70,94,13,109,193,1,120,5,181,113,198,118,50,3,81,70,94,13,109,193,185,120,5,181,113,198,118,153,3,81,70,94,13,109,193,185,120,5,181,113,198,118,153,50,16,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,113,92,2,119,128,0,0,108,0,0,1,107,0,114,69,3,12,1,11,97,31,113,150,64,104,132,61,64,104,12,3,11,97,31,113,150,64,104,132,61,64,104,12,1,11,97,31,115,150,64,104,116,73,64,104,0,0,0,0,0,0,65,149,16,61,65,149,16,61,1,241,33,4,0,33,4,4,10,6,10,181,4,10,6,10,181,38,15,99,111,109,109,97,110,100,101,114,45,97,112,110,45,49,3,99,111,109,5,109,110,99,57,57,6,109,99,99,50,52,48,4,103,112,114,115,8,0,106>>, + B = + <<131,104,19,100,0,13,99,95,99,105,100,95,99,115,103,115,110,95,50,97, + 1,107,0,4,208,161,246,29,107,0,3,237,166,224,107,0,6,66,240,153,0,2, + 10,1,0,8,97,116,116,97,99,104,101,100,104,2,100,0,22,117,112,100,97, + 116,101,95,112,100,112,95,99,111,110,116,101,120,116,95,114,101,113, + 107,0,114,69,3,12,1,11,97,31,113,150,64,104,132,61,64,104,12,3,197, + 31,113,150,64,104,132,61,64,104,12,1,11,97,31,115,150,64,104,116,73, + 64,104,0,0,0,0,0,0,65,149,16,61,65,149,16,61,1,241,33,4,5,0,33,4,4,10 + ,6,10,181,4,10,6,10,181,38,15,99,111,109,109,97,110,100,1,114,45,97, + 112,110,45,49,3,99,111,109,5,109,110,99,57,57,6,109,99,99,50,52,48,4, + 103,112,114,115,8,0,104,2,104,2,100,0,8,97,99,116,105,118,97,116,101, + 104,23,100,0,11,112,100,112,95,99,111,110,116,1,120,116,100,0,7,112, + 114,105,109,97,114,121,97,1,100,0,9,117,110,100,101,102,105,110,101, + 100,97,1,97,4,97,4,97,7,100,0,9,117,110,100,101,102,105,110,101,100, + 100,0,9,117,110,100,101,102,105,110,10100,100,0,9,117,110,100,101, + 102,105,110,101,100,100,0,5,102,97,108,115,101,100,0,9,117,110,100, + 101,102,105,110,101,100,100,0,9,117,110,100,101,102,105,110,101,100, + 100,0,9,117,110,100,101,102,105,1,101,100,97,0,100,0,9,117,110,100, + 101,102,105,110,101,100,107,0,4,16,0,1,144,107,0,4,61,139,186,181, + 107,0,4,10,8,201,49,100,0,9,117,110,100,101,102,105,110,101,100,100, + 0,9,117,110,100,101,102,105,0,101,100,100,0,9,117,110,100,101,102, + 105,110,101,100,104,2,104,3,98,0,0,7,214,97,11,97,20,104,3,97,17,97, + 16,97,21,106,108,0,0,0,3,104,2,97,1,104,2,104,3,98,0,0,7,214,97,11, + 97,20,104,3,97,17,97,167,20,104,2,97,4,104,2,104,3,98,0,0,7,214,97, + 11,97,20,104,3,97,17,97,16,97,21,104,2,97,10,104,2,104,3,98,0,0,7, + 214,97,11,97,20,104,3,97,17,97,16,97,26,106,100,0,5,118,101,114,57, + 57,100,0,9,117,110,0,101,102,105,110,101,100,107,0,2,0,244,107,0,4, + 10,6,102,195,107,0,4,10,6,102,195,100,0,9,117,110,100,101,102,105, + 110,101,100,100,0,9,117,110,100,101,102,105,110,101,100,107,0,125, + 248,143,0,203,25115,157,116,65,185,65,172,55,87,164,88,225,50,203, + 251,115,157,116,65,185,65,172,55,87,164,88,225,50,0,0,82,153,50,0, + 200,98,87,148,237,193,185,65,149,167,69,144,14,16,153,50,3,81,70,94, + 13,109,193,1,120,5,181,113,198,118,50,3,81,70,94,13,109,193,185,120, + 5,181,113,198,118,153,3,81,70,94,13,109,193,185,120,5,181,113,198, + 118,153,50,16,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,113,92,2,119,128,0,0, + 108,0,0,1,107,0,114,69,3,12,1,11,97,31,113,150,64,104,132,61,64,104, + 12,3,11,97,31,113,150,64,104,132,61,64,104,12,1,11,97,31,115,150,64, + 104,116,73,64,104,0,0,0,0,0,0,65,149,16,61,65,149,16,61,1,241,33,4,0, + 33,4,4,10,6,10,181,4,10,6,10,181,38,15,99,111,109,109,97,110,100,101, + 114,45,97,112,110,45,49,3,99,111,109,5,109,110,99,57,57,6,109,99,99, + 50,52,48,4,103,112,114,115,8,0,106>>, + Z = zlib:zip(B), ?m(B, zlib:unzip(Z)). -%% api_g_un_zip_file(doc) -> "Test gunzip_file"; -%% api_g_un_zip_file(suite) -> []; -%% api_g_un_zip_file(Config) when is_list(Config) -> -%% ?line Out = conf(data_dir,Config), -%% io:format("Using OutDir ~p ~n", [Out]), -%% F = filename:join(Out,"testing1"), -%% Data = <<1,1,255,255,255,1,1>>, -%% ?m(ok, file:write_file(F,Data)), -%% ?line Compressed = zlib:gzip_file(F), -%% ?m(ok, file:write_file(F++".gz",Compressed)), -%% ?m(Data, zlib:gunzip_file(F++".gz")), -%% ?m({error,enoent}, zlib:gunzip_file(gurka)), -%% ?m({error,enoent}, zlib:gzip_file(gurka)), -%% ?m({error,what}, zlib:gunzip_file(F)), -%% ?line ok. - -api_g_un_zip(doc) -> "Test gunzip"; -api_g_un_zip(suite) -> []; +%% Test gunzip. api_g_un_zip(Config) when is_list(Config) -> - ?m(?BARG,zlib:gzip(not_a_binary)), + ?m(?EXIT(badarg),zlib:gzip(not_a_binary)), Bin = <<1,11,1,23,45>>, - ?line Comp = zlib:gzip(Bin), + Comp = zlib:gzip(Bin), + ?m(Comp, zlib:gzip(binary_to_list(Bin))), - ?m(?BARG, zlib:gunzip(not_a_binary)), - ?m(?DATA_ERROR, zlib:gunzip(<<171,171,171,171,171>>)), - ?m(?DATA_ERROR, zlib:gunzip(<<>>)), + ?m(?EXIT(badarg), zlib:gunzip(not_a_binary)), + ?m(?EXIT(data_error), zlib:gunzip(<<171,171,171,171,171>>)), + ?m(?EXIT(data_error), zlib:gunzip(<<>>)), ?m(Bin, zlib:gunzip(Comp)), ?m(Bin, zlib:gunzip(binary_to_list(Comp))), - + + %% RFC 1952: + %% + %% "A gzip file consists of a series of "members" (compressed data + %% sets). [...] The members simply appear one after another in the file, + %% with no additional information before, between, or after them." + Concatenated = <<Bin/binary, Bin/binary>>, + ?m(Concatenated, zlib:gunzip([Comp, Comp])), + + %% Don't explode if the uncompressed size is a perfect multiple of the + %% internal inflate chunk size. + ChunkSizedData = <<0:16384/unit:8>>, + ?m(ChunkSizedData, zlib:gunzip(zlib:gzip(ChunkSizedData))), + %% Bad CRC; bad length. BadCrc = bad_crc_data(), - ?line ?m({'EXIT',{data_error,_}},(catch zlib:gunzip(BadCrc))), + ?m(?EXIT(data_error),(catch zlib:gunzip(BadCrc))), BadLen = bad_len_data(), - ?line ?m({'EXIT',{data_error,_}},(catch zlib:gunzip(BadLen))), + ?m(?EXIT(data_error),(catch zlib:gunzip(BadLen))), ok. bad_crc_data() -> @@ -532,76 +707,57 @@ bad_len_data() -> <<31,139,8,0,0,0,0,0,0,3,211,2,0,91,38,185,9,2,0,0,0>>. -intro(suite) -> []; -intro(doc) -> ""; intro(Config) when is_list(Config) -> D = <<"This is a binary">>, [put({ex, N}, <<"This is a binary">>) || N <- [0,1,2,3,4]], put({ex, 5}, end_of_data), put(ex,0), - ?line Read = fun() -> - N = get(ex), - put(ex,N+1), - get({ex,N}) - end, - - ?line Z = zlib:open(), - ?line ok = zlib:deflateInit(Z,default), - - ?line Compress = fun(end_of_data, _Cont) -> []; - (Data, Cont) -> - [zlib:deflate(Z, Data)|Cont(Read(),Cont)] - end, - ?line Compressed = Compress(Read(),Compress), - ?line Last = zlib:deflate(Z, [], finish), - ?line ok = zlib:deflateEnd(Z), - ?line zlib:close(Z), - ?line Res = list_to_binary([Compressed|Last]), + Read = fun() -> + N = get(ex), + put(ex,N+1), + get({ex,N}) + end, + + Z = zlib:open(), + ok = zlib:deflateInit(Z,default), + + Compress = fun(end_of_data, _Cont) -> []; + (Data, Cont) -> + [zlib:deflate(Z, Data)|Cont(Read(),Cont)] + end, + Compressed = Compress(Read(),Compress), + Last = zlib:deflate(Z, [], finish), + ok = zlib:deflateEnd(Z), + zlib:close(Z), + Res = list_to_binary([Compressed|Last]), Orig = list_to_binary(lists:duplicate(5, D)), ?m(Orig, zlib:uncompress(Res)). -large_deflate(doc) -> "Test deflate large file, which had a bug reported on erlang-bugs"; -large_deflate(suite) -> []; +%% Test deflate large file, which had a bug reported on erlang-bugs. large_deflate(Config) when is_list(Config) -> large_deflate_do(). large_deflate_do() -> - ?line Z = zlib:open(), - ?line Plain = rand_bytes(zlib:getBufSize(Z)*5), - ?line ok = zlib:deflateInit(Z), - ?line _ZlibHeader = zlib:deflate(Z, [], full), - ?line Deflated = zlib:deflate(Z, Plain, full), - ?m(ok, zlib:close(Z)), - ?m(Plain, zlib:unzip(list_to_binary([Deflated, 3, 0]))). - -rand_bytes(Sz) -> - L = <<8,2,3,6,1,2,3,2,3,4,8,7,3,7,2,3,4,7,5,8,9,3>>, - rand_bytes(erlang:md5(L),Sz). - -rand_bytes(Bin, Sz) when byte_size(Bin) >= Sz -> - <<Res:Sz/binary, _/binary>> = Bin, - Res; -rand_bytes(Bin, Sz) -> - rand_bytes(<<(erlang:md5(Bin))/binary, Bin/binary>>, Sz). - - -zip_usage(doc) -> "Test a standard compressed zip file"; -zip_usage(suite) -> []; + Plain = gen_determ_rand_bytes(64 bsl 10), + Deflated = zlib:zip(Plain), + ?m(Plain, zlib:unzip(Deflated)). + +%% Test a standard compressed zip file. zip_usage(Config) when is_list(Config) -> zip_usage(zip_usage({get_arg,Config})); zip_usage({get_arg,Config}) -> - ?line Out = conf(data_dir,Config), - ?line {ok,ZIP} = file:read_file(filename:join(Out,"zipdoc.zip")), - ?line {ok,ORIG} = file:read_file(filename:join(Out,"zipdoc")), + Out = get_data_dir(Config), + {ok,ZIP} = file:read_file(filename:join(Out,"zipdoc.zip")), + {ok,ORIG} = file:read_file(filename:join(Out,"zipdoc")), {run,ZIP,ORIG}; zip_usage({run,ZIP,ORIG}) -> - ?line <<_:14/binary, CRC:32/little, - CompSz:32/little, UnCompSz:32/little,_:31/binary, - Compressed:CompSz/binary, _/binary>> = ZIP, - + <<_:14/binary, CRC:32/little, + CompSz:32/little, UnCompSz:32/little,_:31/binary, + Compressed:CompSz/binary, _/binary>> = ZIP, + %%io:format("CRC ~p CSz ~p UnCSz ~p ~n", [CRC,CompSz,UnCompSz]), - ?line Split = split_bin(Compressed,[]), - ?line Z = zlib:open(), + Split = split_bin(Compressed,[]), + Z = zlib:open(), ?m(ok, zlib:inflateInit(Z, -15)), Bs = [zlib:inflate(Z, Part) || Part <- Split], @@ -611,86 +767,84 @@ zip_usage({run,ZIP,ORIG}) -> ?m(true, zlib:crc32(Z,UC0) == zlib:crc32(Z,ORIG)), ?m(ok, zlib:inflateEnd(Z)), - ?line UC1 = zlib:unzip(Compressed), + UC1 = zlib:unzip(Compressed), ?m(UnCompSz, byte_size(UC1)), ?m(true, zlib:crc32(Z,UC1) == zlib:crc32(Z,ORIG)), - + ?m(ok, zlib:inflateInit(Z, -15)), - ?line UC2 = zlib:inflate(Z, Compressed), + UC2 = zlib:inflate(Z, Compressed), ?m(UnCompSz, byte_size(list_to_binary(UC2))), ?m(CRC, zlib:crc32(Z)), ?m(true, zlib:crc32(Z,UC2) == zlib:crc32(Z,ORIG)), ?m(ok, zlib:inflateEnd(Z)), - + ?m(ok, zlib:inflateInit(Z, -15)), - ?line UC3 = zlib:inflate(Z, Split), % Test multivec. + UC3 = zlib:inflate(Z, Split), % Test multivec. ?m(UnCompSz, byte_size(list_to_binary(UC3))), ?m(true, zlib:crc32(Z,UC3) == zlib:crc32(Z,ORIG)), ?m(CRC, zlib:crc32(Z)), ?m(ok, zlib:inflateEnd(Z)), - + ?m(ok, zlib:inflateInit(Z, -15)), ?m(ok, zlib:setBufSize(Z, UnCompSz *2)), - ?line UC4 = zlib:inflate(Z, Compressed), + UC4 = zlib:inflate(Z, Compressed), ?m(UnCompSz, byte_size(list_to_binary(UC4))), ?m(CRC, zlib:crc32(Z)), ?m(CRC, zlib:crc32(Z,UC4)), ?m(true, zlib:crc32(Z,UC4) == zlib:crc32(Z,ORIG)), ?m(ok, zlib:inflateEnd(Z)), - - ?line C1 = zlib:zip(ORIG), - ?line UC5 = zlib:unzip(C1), + + C1 = zlib:zip(ORIG), + UC5 = zlib:unzip(C1), ?m(CRC, zlib:crc32(Z,UC5)), ?m(true,zlib:crc32(Z,UC5) == zlib:crc32(Z,ORIG)), - + ?m(ok, zlib:deflateInit(Z, default, deflated, -15, 8, default)), - ?line C2 = zlib:deflate(Z, ORIG, finish), - ?m(true, C1 == list_to_binary(C2)), + C2 = zlib:deflate(Z, ORIG, finish), + ?m(ORIG, zlib:unzip(C2)), ?m(ok, zlib:deflateEnd(Z)), - + ?m(ok, zlib:deflateInit(Z, none, deflated, -15, 8, filtered)), ?m(ok, zlib:deflateParams(Z, default, default)), - ?line C3 = zlib:deflate(Z, ORIG, finish), - ?m(true, C1 == list_to_binary(C3)), + C3 = zlib:deflate(Z, ORIG, finish), + ?m(ORIG, zlib:unzip(C3)), ?m(ok, zlib:deflateEnd(Z)), - ?line ok = zlib:close(Z), - ?line ok. + ok = zlib:close(Z), + ok. -gz_usage(doc) -> "Test a standard compressed gzipped file"; -gz_usage(suite) -> []; +%% Test a standard compressed gzipped file. gz_usage(Config) when is_list(Config) -> gz_usage(gz_usage({get_arg,Config})); gz_usage({get_arg,Config}) -> - ?line Out = conf(data_dir,Config), - ?line {ok,GZIP} = file:read_file(filename:join(Out,"zipdoc.1.gz")), - ?line {ok,ORIG} = file:read_file(filename:join(Out,"zipdoc")), - ?line {ok,GZIP2} = file:read_file(filename:join(Out,"zipdoc.txt.gz")), + Out = get_data_dir(Config), + {ok,GZIP} = file:read_file(filename:join(Out,"zipdoc.1.gz")), + {ok,ORIG} = file:read_file(filename:join(Out,"zipdoc")), + {ok,GZIP2} = file:read_file(filename:join(Out,"zipdoc.txt.gz")), {run,GZIP,ORIG,GZIP2}; gz_usage({run,GZIP,ORIG,GZIP2}) -> - ?line Z = zlib:open(), - ?line UC1 = zlib:gunzip(GZIP), + Z = zlib:open(), + UC1 = zlib:gunzip(GZIP), ?m(true,zlib:crc32(Z,UC1) == zlib:crc32(Z,ORIG)), - ?line UC3 = zlib:gunzip(GZIP2), + UC3 = zlib:gunzip(GZIP2), ?m(true,zlib:crc32(Z,UC3) == zlib:crc32(Z,ORIG)), - ?line Compressed = zlib:gzip(ORIG), - ?line UC5 = zlib:gunzip(Compressed), + Compressed = zlib:gzip(ORIG), + UC5 = zlib:gunzip(Compressed), ?m(true,zlib:crc32(Z,UC5) == zlib:crc32(Z,ORIG)), - ?line ok = zlib:close(Z). + ok = zlib:close(Z). -gz_usage2(doc) -> "Test more of a standard compressed gzipped file"; -gz_usage2(suite) -> []; +%% Test more of a standard compressed gzipped file. gz_usage2(Config) -> case os:find_executable("gzip") of Name when is_list(Name) -> - ?line Z = zlib:open(), - ?line Out = conf(data_dir,Config), - ?line {ok,ORIG} = file:read_file(filename:join(Out,"zipdoc")), - ?line Compressed = zlib:gzip(ORIG), + Z = zlib:open(), + Out = get_data_dir(Config), + {ok,ORIG} = file:read_file(filename:join(Out,"zipdoc")), + Compressed = zlib:gzip(ORIG), GzOutFile = filename:join(Out,"out.gz"), OutFile = filename:join(Out,"out.txt"), ?m(ok, file:write_file(GzOutFile,Compressed)), - ?line os:cmd("gzip -c -d " ++ GzOutFile ++ " > " ++ OutFile), + os:cmd("gzip -c -d " ++ GzOutFile ++ " > " ++ OutFile), case file:read_file(OutFile) of {ok,ExtDecompressed} -> ?m(true, @@ -699,83 +853,80 @@ gz_usage2(Config) -> io:format("Couldn't test external decompressor ~p\n", [Error]) end, - ?line ok = zlib:close(Z), + ok = zlib:close(Z), ok; false -> {skipped,"No gzip in path"} end. - -compress_usage(doc) -> - "Test that (de)compress funcs work with" - " standard tools, for example a chunk from a png file"; -compress_usage(suite) -> []; + +%% Test that (de)compress funcs work with standard tools, for example +%% a chunk from a png file. compress_usage(Config) when is_list(Config) -> compress_usage(compress_usage({get_arg,Config})); compress_usage({get_arg,Config}) -> - ?line Out = conf(data_dir,Config), - ?line {ok,C1} = file:read_file(filename:join(Out,"png-compressed.zlib")), + Out = get_data_dir(Config), + {ok,C1} = file:read_file(filename:join(Out,"png-compressed.zlib")), {run,C1}; compress_usage({run,C1}) -> - ?line Z = zlib:open(), + Z = zlib:open(), %% See that we can uncompress a file generated with external prog. - ?line UC1 = zlib:uncompress(C1), + UC1 = zlib:uncompress(C1), %% Check that the crc are correct. ?m(4125865008,zlib:crc32(Z,UC1)), - ?line C2 = zlib:compress(UC1), - ?line UC2 = zlib:uncompress(C2), + C2 = zlib:compress(UC1), + UC2 = zlib:uncompress(C2), %% Check that the crc are correct. ?m(4125865008,zlib:crc32(Z,UC2)), - - ?line ok = zlib:close(Z), + + ok = zlib:close(Z), D = [<<"We tests some partial">>, <<"data, sent over">>, <<"the stream">>, <<"we check that we can unpack">>, <<"every message we get">>], - - ?line ZC = zlib:open(), - ?line ZU = zlib:open(), + + ZC = zlib:open(), + ZU = zlib:open(), Test = fun(finish, {_,Tot}) -> - ?line Compressed = zlib:deflate(ZC, <<>>, finish), + Compressed = zlib:deflate(ZC, <<>>, finish), Data = zlib:inflate(ZU, Compressed), [Tot|Data]; (Data, {Op,Tot}) -> - ?line Compressed = zlib:deflate(ZC, Data, Op), + Compressed = zlib:deflate(ZC, Data, Op), Res1 = ?m([Data],zlib:inflate(ZU, Compressed)), {Op, [Tot|Res1]} end, - ?line zlib:deflateInit(ZC), - ?line zlib:inflateInit(ZU), - ?line T1 = lists:foldl(Test,{sync,[]},D++[finish]), + zlib:deflateInit(ZC), + zlib:inflateInit(ZU), + T1 = lists:foldl(Test,{sync,[]},D++[finish]), ?m(true, list_to_binary(D) == list_to_binary(T1)), - ?line zlib:deflateEnd(ZC), - ?line zlib:inflateEnd(ZU), - - ?line zlib:deflateInit(ZC), - ?line zlib:inflateInit(ZU), - ?line T2 = lists:foldl(Test,{full,[]},D++[finish]), + zlib:deflateEnd(ZC), + zlib:inflateEnd(ZU), + + zlib:deflateInit(ZC), + zlib:inflateInit(ZU), + T2 = lists:foldl(Test,{full,[]},D++[finish]), ?m(true, list_to_binary(D) == list_to_binary(T2)), - ?line zlib:deflateEnd(ZC), - ?line zlib:inflateEnd(ZU), - - ?line ok = zlib:close(ZC), - ?line ok = zlib:close(ZU). + zlib:deflateEnd(ZC), + zlib:inflateEnd(ZU), + + ok = zlib:close(ZC), + ok = zlib:close(ZU). -crc(doc) -> "Check that crc works as expected"; -crc(suite) -> []; +%% Check that crc works as expected. crc(Config) when is_list(Config) -> crc(crc({get_arg,Config})); crc({get_arg,Config}) -> - ?line Out = conf(data_dir,Config), - ?line {ok,C1} = file:read_file(filename:join(Out,"zipdoc")), + Out = get_data_dir(Config), + {ok,C1} = file:read_file(filename:join(Out,"zipdoc")), {run,C1}; crc({run,C1}) -> - ?line Z = zlib:open(), - ?line Crc = zlib:crc32(Z, C1), + Z = zlib:open(), + Crc = zlib:crc32(Z, C1), Bins = split_bin(C1,[]), %%io:format("Length ~p ~p ~n", [length(Bins), [size(Bin) || Bin <- Bins]]), Last = lists:last(Bins), @@ -785,29 +936,28 @@ crc({run,C1}) -> Crc1 end, 0, Bins), ?m(Crc,SCrc), - ?line [First|Rest] = Bins, + [First|Rest] = Bins, Combine = fun(Bin, CS1) -> CS2 = zlib:crc32(Z, Bin), S2 = byte_size(Bin), zlib:crc32_combine(Z,CS1,CS2,S2) end, - ?line Comb = lists:foldl(Combine, zlib:crc32(Z, First), Rest), + Comb = lists:foldl(Combine, zlib:crc32(Z, First), Rest), ?m(Crc,Comb), - ?line ok = zlib:close(Z). + ok = zlib:close(Z). -adler(doc) -> "Check that adler works as expected"; -adler(suite) -> []; +%% Check that adler works as expected. adler(Config) when is_list(Config) -> adler(adler({get_arg,Config})); adler({get_arg,Config}) -> - ?line Out = conf(data_dir,Config), + Out = get_data_dir(Config), File1 = filename:join(Out,"zipdoc"), - ?line {ok,C1} = file:read_file(File1), + {ok,C1} = file:read_file(File1), {run,C1}; adler({run,C1}) -> - ?line Z = zlib:open(), + Z = zlib:open(), ?m(1, zlib:adler32(Z,<<>>)), - ?line Crc = zlib:adler32(Z, C1), + Crc = zlib:adler32(Z, C1), Bins = split_bin(C1,[]), Last = lists:last(Bins), SCrc = lists:foldl(fun(Bin,Crc0) -> @@ -816,43 +966,46 @@ adler({run,C1}) -> Crc1 end, zlib:adler32(Z,<<>>), Bins), ?m(Crc,SCrc), - ?line [First|Rest] = Bins, + [First|Rest] = Bins, Combine = fun(Bin, CS1) -> CS2 = zlib:adler32(Z, Bin), S2 = byte_size(Bin), zlib:adler32_combine(Z,CS1,CS2,S2) end, - ?line Comb = lists:foldl(Combine, zlib:adler32(Z, First), Rest), + Comb = lists:foldl(Combine, zlib:adler32(Z, First), Rest), ?m(Crc,Comb), - ?line ok = zlib:close(Z). + ok = zlib:close(Z). -dictionary_usage(doc) -> "Test dictionary usage"; -dictionary_usage(suite) -> []; +%% Test dictionary usage. dictionary_usage(Config) when is_list(Config) -> dictionary_usage(dictionary_usage({get_arg,Config})); dictionary_usage({get_arg,_Config}) -> {run}; % no args dictionary_usage({run}) -> - ?line Z1 = zlib:open(), + Z1 = zlib:open(), Dict = <<"Anka">>, Data = <<"Kalle Anka">>, ?m(ok, zlib:deflateInit(Z1)), - ?line DictID = zlib:deflateSetDictionary(Z1, Dict), - %% ?line io:format("DictID = ~p\n", [DictID]), - ?line B1 = zlib:deflate(Z1, Data), - ?line B2 = zlib:deflate(Z1, <<>>, finish), + DictID = zlib:deflateSetDictionary(Z1, Dict), + %% io:format("DictID = ~p\n", [DictID]), + B1 = zlib:deflate(Z1, Data), + B2 = zlib:deflate(Z1, <<>>, finish), ?m(ok, zlib:deflateEnd(Z1)), ?m(ok, zlib:close(Z1)), Compressed = list_to_binary([B1,B2]), %% io:format("~p\n", [Compressed]), %% Now uncompress. - ?line Z2 = zlib:open(), + Z2 = zlib:open(), ?m(ok, zlib:inflateInit(Z2)), - ?line {'EXIT',{{need_dictionary,DictID},_}} = (catch zlib:inflate(Z2, Compressed)), + + ?m(?EXIT({need_dictionary, DictID}), zlib:inflate(Z2, Compressed)), + ?m(ok, zlib:inflateSetDictionary(Z2, Dict)), ?m(ok, zlib:inflateSetDictionary(Z2, binary_to_list(Dict))), - ?line Uncompressed = ?m(B when is_list(B), zlib:inflate(Z2, [])), + + Uncompressed = ?m(B when is_list(B), zlib:inflate(Z2, [])), + ?m(ok, zlib:inflateEnd(Z2)), ?m(ok, zlib:close(Z2)), ?m(Data, list_to_binary(Uncompressed)). @@ -862,38 +1015,77 @@ split_bin(<<Part:1997/binary,Rest/binary>>, Acc) -> split_bin(Last,Acc) -> lists:reverse([Last|Acc]). +only_allow_owner(Config) when is_list(Config) -> + Z = zlib:open(), + Owner = self(), + + ?m(ok, zlib:inflateInit(Z)), + ?m(ok, zlib:inflateReset(Z)), + + {Pid, Ref} = spawn_monitor( + fun() -> + ?m(?EXIT(not_on_controlling_process), zlib:inflateReset(Z)), + Owner ! '$transfer_ownership', + receive + '$ownership_transferred' -> + ?m(ok, zlib:inflateReset(Z)) + after 200 -> + ct:fail("Never received transfer signal.") + end + end), + ownership_transfer_check(Z, Pid, Ref). + +ownership_transfer_check(Z, WorkerPid, Ref) -> + receive + '$transfer_ownership' -> + zlib:set_controlling_process(Z, WorkerPid), + WorkerPid ! '$ownership_transferred', + ownership_transfer_check(Z, WorkerPid, Ref); + {'DOWN', Ref, process, WorkerPid, normal} -> + ok; + {'DOWN', Ref, process, WorkerPid, Reason} -> + ct:fail("Spawned worker crashed with reason ~p.", [Reason]) + after 200 -> + ct:fail("Spawned worker timed out.") + end. + +sub_heap_binaries(Config) when is_list(Config) -> + Compressed = zlib:compress(<<"gurka">>), + ConfLen = erlang:length(Config), -smp(doc) -> "Check concurrent access to zlib driver"; -smp(suite) -> []; + HeapBin = <<ConfLen:8/integer, Compressed/binary>>, + <<_:8/integer, SubHeapBin/binary>> = HeapBin, + + ?m(<<"gurka">>, zlib:uncompress(SubHeapBin)), + ok. + +%% Check concurrent access to zlib driver. smp(Config) -> - case erlang:system_info(smp_support) of - true -> - NumOfProcs = lists:min([8,erlang:system_info(schedulers)]), - io:format("smp starting ~p workers\n",[NumOfProcs]), - - %% Tests to run in parallel. - Funcs = [zip_usage, gz_usage, compress_usage, dictionary_usage, - crc, adler], - - %% We get all function arguments here to avoid repeated parallel - %% file read access. - FnAList = lists:map(fun(F) -> {F,?MODULE:F({get_arg,Config})} - end, Funcs), - - Pids = [spawn_link(?MODULE, worker, [random:uniform(9999), - list_to_tuple(FnAList), - self()]) - || _ <- lists:seq(1,NumOfProcs)], - wait_pids(Pids); + NumOfProcs = lists:min([8,erlang:system_info(schedulers)]), + io:format("smp starting ~p workers\n",[NumOfProcs]), - false -> - {skipped,"No smp support"} - end. - + %% Tests to run in parallel. + Funcs = + [zip_usage, gz_usage, compress_usage, dictionary_usage, + crc, adler], + + %% We get all function arguments here to avoid repeated parallel + %% file read access. + UsageArgs = + list_to_tuple([{F, ?MODULE:F({get_arg,Config})} || F <- Funcs]), + Parent = self(), + + WorkerFun = + fun() -> + worker(rand:uniform(9999), UsageArgs, Parent) + end, + + Pids = [spawn_link(WorkerFun) || _ <- lists:seq(1, NumOfProcs)], + wait_pids(Pids). worker(Seed, FnATpl, Parent) -> io:format("smp worker ~p, seed=~p~n",[self(),Seed]), - random:seed(Seed,Seed,Seed), + rand:seed(exsplus, {Seed,Seed,Seed}), worker_loop(100, FnATpl), Parent ! self(). @@ -901,33 +1093,32 @@ worker_loop(0, _FnATpl) -> large_deflate_do(), % the time consuming one as finale ok; worker_loop(N, FnATpl) -> - {F,A} = element(random:uniform(size(FnATpl)),FnATpl), + {F,A} = element(rand:uniform(tuple_size(FnATpl)), FnATpl), ?MODULE:F(A), worker_loop(N-1, FnATpl). - + wait_pids([]) -> ok; wait_pids(Pids) -> receive Pid -> - ?line true = lists:member(Pid,Pids), + true = lists:member(Pid,Pids), Others = lists:delete(Pid,Pids), io:format("wait_pid got ~p, still waiting for ~p\n",[Pid,Others]), wait_pids(Others) end. -otp_7359(doc) -> "Deflate/inflate data with size close to multiple of internal buffer size"; -otp_7359(suite) -> []; +%% Deflate/inflate data with size close to multiple of internal buffer size. otp_7359(_Config) -> %% Find compressed size ZTry = zlib:open(), ok = zlib:deflateInit(ZTry), ISize = zlib:getBufSize(ZTry), IData = list_to_binary([Byte band 255 || Byte <- lists:seq(1,ISize)]), - ?line ISize = byte_size(IData), + ISize = byte_size(IData), - ?line DSize = iolist_size(zlib:deflate(ZTry, IData, sync)), + DSize = iolist_size(zlib:deflate(ZTry, IData, sync)), zlib:close(ZTry), io:format("Deflated try ~p -> ~p bytes~n", [ISize, DSize]), @@ -949,19 +1140,19 @@ otp_7359(_Config) -> otp_7359_def_inf(Data,{DefSize,InfSize}) -> %%io:format("Try: DefSize=~p InfSize=~p~n", [DefSize,InfSize]), - ?line ZDef = zlib:open(), - ?line ok = zlib:deflateInit(ZDef), - ?line ok = zlib:setBufSize(ZDef,DefSize), - ?line DefData = iolist_to_binary(zlib:deflate(ZDef, Data, sync)), + ZDef = zlib:open(), + ok = zlib:deflateInit(ZDef), + ok = zlib:setBufSize(ZDef,DefSize), + DefData = iolist_to_binary(zlib:deflate(ZDef, Data, sync)), %%io:format("Deflated ~p(~p) -> ~p(~p) bytes~n", %% [byte_size(Data), InfSize, byte_size(DefData), DefSize]), - ?line ok = zlib:close(ZDef), + ok = zlib:close(ZDef), - ?line ZInf = zlib:open(), - ?line ok = zlib:inflateInit(ZInf), - ?line ok = zlib:setBufSize(ZInf,InfSize), - ?line Data = iolist_to_binary(zlib:inflate(ZInf, DefData)), - ?line ok = zlib:close(ZInf), + ZInf = zlib:open(), + ok = zlib:inflateInit(ZInf), + ok = zlib:setBufSize(ZInf,InfSize), + Data = iolist_to_binary(zlib:inflate(ZInf, DefData)), + ok = zlib:close(ZInf), ok. otp_9981(Config) when is_list(Config) -> @@ -981,43 +1172,98 @@ otp_9981(Config) when is_list(Config) -> Ports = lists:sort(erlang:ports()), ok. - +-define(BENCH_SIZE, (16 bsl 20)). + +-define(DECOMPRESS_BENCH(Name, What, Data), + Name(Config) when is_list(Config) -> + Uncompressed = Data, + Compressed = zlib:compress(Uncompressed), + What(Compressed, byte_size(Uncompressed))). + +-define(COMPRESS_BENCH(Name, What, Data), + Name(Config) when is_list(Config) -> + Compressed = Data, + What(Compressed, byte_size(Compressed))). + +?DECOMPRESS_BENCH(inflate_bench_zeroed, throughput_bench_inflate, + <<0:(8 * ?BENCH_SIZE)>>). +?DECOMPRESS_BENCH(inflate_bench_rand, throughput_bench_inflate, + gen_determ_rand_bytes(?BENCH_SIZE)). + +?DECOMPRESS_BENCH(chunk_bench_zeroed, throughput_bench_chunk, + <<0:(8 * ?BENCH_SIZE)>>). +?DECOMPRESS_BENCH(chunk_bench_rand, throughput_bench_chunk, + gen_determ_rand_bytes(?BENCH_SIZE)). + +?COMPRESS_BENCH(deflate_bench_zeroed, throughput_bench_deflate, + <<0:(8 * ?BENCH_SIZE)>>). +?COMPRESS_BENCH(deflate_bench_rand, throughput_bench_deflate, + gen_determ_rand_bytes(?BENCH_SIZE)). + +throughput_bench_inflate(Compressed, Size) -> + Z = zlib:open(), + zlib:inflateInit(Z), + + submit_throughput_results(Size, + fun() -> + zlib:inflate(Z, Compressed) + end). + +throughput_bench_deflate(Uncompressed, Size) -> + Z = zlib:open(), + zlib:deflateInit(Z), + + submit_throughput_results(Size, + fun() -> + zlib:deflate(Z, Uncompressed, finish) + end). + +throughput_bench_chunk(Compressed, Size) -> + Z = zlib:open(), + zlib:inflateInit(Z), + + ChunkLoop = + fun + Loop({more, _}) -> Loop(zlib:inflateChunk(Z)); + Loop(_) -> ok + end, + + submit_throughput_results(Size, + fun() -> + ChunkLoop(zlib:inflateChunk(Z, Compressed)) + end). + +submit_throughput_results(Size, Fun) -> + TimeTaken = measure_perf_counter(Fun, millisecond), + + KBPS = trunc((Size bsr 10) / (TimeTaken / 1000)), + ct_event:notify(#event{ name = benchmark_data, data = [{value,KBPS}] }), + {comment, io_lib:format("~p ms, ~p KBPS", [TimeTaken, KBPS])}. + +measure_perf_counter(Fun, Unit) -> + Start = os:perf_counter(Unit), + Fun(), + os:perf_counter(Unit) - Start. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% Helps with testing directly %%%%%%%%%%%%% -conf(What,Config) -> - try ?config(What,Config) of - undefined -> - "./zlib_SUITE_data"; - Dir -> - Dir +get_data_dir(Config) -> + try proplists:get_value(data_dir,Config) of + undefined -> + "./zlib_SUITE_data"; + Dir -> + Dir catch - _:_ -> "./zlib_SUITE_data" + _:_ -> "./zlib_SUITE_data" end. -t() -> t([all]). - -t(What) when not is_list(What) -> - t([What]); -t(What) -> - lists:foreach(fun(T) -> - try ?MODULE:T([]) - catch _E:_R -> - Line = get(test_server_loc), - io:format("Failed ~p:~p ~p ~p ~p~n", - [T,Line,_E,_R, erlang:get_stacktrace()]) - end - end, expand(What)). - -expand(All) -> - lists:reverse(expand(All,[])). -expand([H|T], Acc) -> - case ?MODULE:H(suite) of - [] -> expand(T,[H|Acc]); - Cs -> - R = expand(Cs, Acc), - expand(T, R) - end; -expand([], Acc) -> Acc. - +%% Generates a bunch of statistically random bytes using the size as seed. +gen_determ_rand_bytes(Size) -> + gen_determ_rand_bytes(Size, erlang:md5_init(), <<>>). +gen_determ_rand_bytes(Size, _Context, Acc) when Size =< 0 -> + Acc; +gen_determ_rand_bytes(Size, Context0, Acc) when Size > 0 -> + Context = erlang:md5_update(Context0, <<Size/integer>>), + Checksum = erlang:md5_final(Context), + gen_determ_rand_bytes(Size - 16, Context, <<Acc/binary, Checksum/binary>>). diff --git a/lib/kernel/test/zzz_SUITE.erl b/lib/kernel/test/zzz_SUITE.erl new file mode 100644 index 0000000000..59c7fd7404 --- /dev/null +++ b/lib/kernel/test/zzz_SUITE.erl @@ -0,0 +1,37 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2006-2018. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% +-module(zzz_SUITE). + +%% The sole purpose of this test suite is for things we want to run last +%% before the VM terminates. + +-export([all/0]). + +-export([lc_graph/1]). + + +all() -> + [lc_graph]. + +lc_graph(_Config) -> + %% Create "lc_graph" file in current working dir + %% if lock checker is enabled. + erts_debug:lc_graph(), + ok. |