From 349c39e875e0af1bf42dcbc171a47d824401840e Mon Sep 17 00:00:00 2001
From: Peter Andersson <peppe@erlang.org>
Date: Mon, 17 Oct 2016 14:52:59 +0200
Subject: Fix problem with printouts to incorrect parent group leader

---
 lib/common_test/src/Makefile          |  1 +
 lib/common_test/src/ct_default_gl.erl | 83 +++++++++++++++++++++++++++++++++++
 lib/common_test/src/ct_framework.erl  | 14 +++---
 lib/common_test/src/ct_logs.erl       | 13 ++++--
 lib/common_test/src/ct_run.erl        | 15 ++++---
 lib/common_test/src/ct_util.erl       | 18 +++++---
 lib/common_test/src/ct_util.hrl       |  1 +
 7 files changed, 122 insertions(+), 23 deletions(-)
 create mode 100644 lib/common_test/src/ct_default_gl.erl

(limited to 'lib')

diff --git a/lib/common_test/src/Makefile b/lib/common_test/src/Makefile
index 0f9e044f9e..9d751996ad 100644
--- a/lib/common_test/src/Makefile
+++ b/lib/common_test/src/Makefile
@@ -80,6 +80,7 @@ MODULES= \
 	ct_groups \
 	ct_property_test \
 	ct_release_test \
+	ct_default_gl \
 	erl2html2 \
 	test_server_ctrl \
 	test_server_gl \
diff --git a/lib/common_test/src/ct_default_gl.erl b/lib/common_test/src/ct_default_gl.erl
new file mode 100644
index 0000000000..d1b52e5f4f
--- /dev/null
+++ b/lib/common_test/src/ct_default_gl.erl
@@ -0,0 +1,83 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2012-2016. 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(ct_default_gl).
+-export([start_link/1, stop/0]).
+
+-export([init/1,handle_call/3,handle_cast/2,handle_info/2,terminate/2]).
+
+%% start_link()
+%% Start a new group leader process.
+start_link(ParentGL) ->
+    do_start(ParentGL, 3).
+
+do_start(_ParentGL, 0) ->
+    exit({?MODULE,startup});
+do_start(ParentGL, Retries) ->
+    case whereis(?MODULE) of
+	undefined ->
+	    case gen_server:start_link(?MODULE, [ParentGL], []) of
+		{ok,Pid} ->
+		    {ok,Pid};
+		Other ->
+		    Other
+	    end;
+	Pid ->
+	    exit(Pid, kill),
+	    timer:sleep(1000),
+	    do_start(ParentGL, Retries-1)
+    end.
+
+%% stop(Pid)
+%% Stop a group leader process.
+stop() ->
+    gen_server:cast(whereis(?MODULE), stop).
+
+
+%%% Internal functions.
+
+init([ParentGL]) ->
+    register(?MODULE, self()),
+    {ok,#{parent_gl_pid => ParentGL,
+	  parent_gl_monitor => erlang:monitor(process,ParentGL)}}.
+
+handle_cast(stop, St) ->
+    {stop,normal,St}.
+
+%% If the parent group leader dies, fall back on using the local user process
+handle_info({'DOWN',Ref,process,_,_Reason}, #{parent_gl_monitor := Ref} = St) ->
+    User = whereis(user),
+    {noreply,St#{parent_gl_pid => User,
+		 parent_gl_monitor => erlang:monitor(process,User)}};
+
+handle_info({io_request,_From,_ReplyAs,_Req} = IoReq,
+	    #{parent_gl_pid := ParentGL} = St) ->
+    ParentGL ! IoReq,
+    {noreply,St};
+
+handle_info(Msg, St) ->
+    io:format(user, "Common Test Group Leader process got: ~tp~n", [Msg]),
+    {noreply,St}.
+
+handle_call(_Req, _From, St) ->
+    {reply,ok,St}.
+
+terminate(_, _) ->
+    ok.
diff --git a/lib/common_test/src/ct_framework.erl b/lib/common_test/src/ct_framework.erl
index 104515e57e..291a4d716c 100644
--- a/lib/common_test/src/ct_framework.erl
+++ b/lib/common_test/src/ct_framework.erl
@@ -307,7 +307,7 @@ add_defaults(Mod,Func, GroupPath) ->
 				   "~w:suite/0 failed: ~p~n",
 				   [Suite,Reason]),
 	    io:format(ErrStr, []),
-	    io:format(user, ErrStr, []),
+	    io:format(?def_gl, ErrStr, []),
 	    {suite0_failed,{exited,Reason}};
 	SuiteInfo when is_list(SuiteInfo) ->
 	    case lists:all(fun(E) when is_tuple(E) -> true;
@@ -330,7 +330,7 @@ add_defaults(Mod,Func, GroupPath) ->
 					   "~w:suite/0: ~p~n",
 					   [Suite,SuiteInfo]),
 		    io:format(ErrStr, []),
-		    io:format(user, ErrStr, []),
+		    io:format(?def_gl, ErrStr, []),
 		    {suite0_failed,bad_return_value}
 	    end;
 	SuiteInfo ->
@@ -338,7 +338,7 @@ add_defaults(Mod,Func, GroupPath) ->
 				   "Invalid return value from "
 				   "~w:suite/0: ~p~n", [Suite,SuiteInfo]),
 	    io:format(ErrStr, []),
-	    io:format(user, ErrStr, []),
+	    io:format(?def_gl, ErrStr, []),
 	    {suite0_failed,bad_return_value}
     end.
 
@@ -366,7 +366,7 @@ add_defaults1(Mod,Func, GroupPath, SuiteInfo) ->
 				      "~w:group(~w): ~p~n",
 				      [Mod,GrName,BadGr0Val]),
 	    io:format(Gr0ErrStr, []),
-	    io:format(user, Gr0ErrStr, []),
+	    io:format(?def_gl, Gr0ErrStr, []),
 	    {group0_failed,bad_return_value};
 	_ ->
 	    Args = if Func == init_per_group ; Func == end_per_group ->
@@ -388,7 +388,7 @@ add_defaults1(Mod,Func, GroupPath, SuiteInfo) ->
 					      "~w:~w/0: ~p~n",
 					      [Mod,Func,BadTC0Val]),
 		    io:format(TC0ErrStr, []),
-		    io:format(user, TC0ErrStr, []),
+		    io:format(?def_gl, TC0ErrStr, []),
 		    {testcase0_failed,bad_return_value};
 		_ ->
 		    %% let test case info (also for all config funcs) override
@@ -927,7 +927,7 @@ error_notification(Mod,Func,_Args,{Error,Loc}) ->
 		       Div = "~n- - - - - - - - - - - - - - - - - - - "
 			     "- - - - - - - - - - - - - - - - - - - - -~n",
 		       ErrorStr2 = io_lib:format(ErrorFormat, ErrorArgs),
-		       io:format(user, lists:concat([Div,ErrorStr2,Div,"~n"]),
+		       io:format(?def_gl, lists:concat([Div,ErrorStr2,Div,"~n"]),
 				 []),
 		       Link =
 			   "\n\n<a href=\"#end\">"
@@ -1133,7 +1133,7 @@ get_all(Mod, ConfTests) ->
 		    ErrStr = io_lib:format("~n*** ERROR *** "
 					   "~w:all/0 failed: ~p~n",
 					   [Mod,ExitReason]),
-		    io:format(user, ErrStr, []),
+		    io:format(?def_gl, ErrStr, []),
 		    %% save the error info so it doesn't get printed twice
 		    ct_util:set_testdata_async({{error_in_suite,Mod},
 						ExitReason});
diff --git a/lib/common_test/src/ct_logs.erl b/lib/common_test/src/ct_logs.erl
index 9282a9f81d..0daed60dba 100644
--- a/lib/common_test/src/ct_logs.erl
+++ b/lib/common_test/src/ct_logs.erl
@@ -531,8 +531,13 @@ tc_print(Category,Importance,Format,Args) ->
 		   Val
 	   end,
     if Importance >= (100-VLvl) ->
-	    Head = get_heading(Category),
-	    io:format(user, lists:concat([Head,Format,"\n\n"]), Args),
+            Str = lists:concat([get_heading(Category),Format,"\n\n"]),
+            try
+                io:format(?def_gl, Str, Args)
+            catch
+                %% default group leader probably not started, or has stopped
+                _:_ -> io:format(user, Str, Args)
+            end,
 	    ok;
        true ->
 	    ok
@@ -679,7 +684,7 @@ logger(Parent, Mode, Verbosity) ->
 	    PrivFilesDestRun = [filename:join(AbsDir, F) || F <- PrivFiles],
 	    case copy_priv_files(PrivFilesSrc, PrivFilesDestTop) of
 		{error,Src1,Dest1,Reason1} ->
-		    io:format(user, "ERROR! "++
+		    io:format(?def_gl, "ERROR! "++
 				  "Priv file ~p could not be copied to ~p. "++
 				  "Reason: ~p~n",
 			      [Src1,Dest1,Reason1]),
@@ -687,7 +692,7 @@ logger(Parent, Mode, Verbosity) ->
 		ok ->
 		    case copy_priv_files(PrivFilesSrc, PrivFilesDestRun) of
 			{error,Src2,Dest2,Reason2} ->
-			    io:format(user,
+			    io:format(?def_gl,
 				      "ERROR! "++
 				      "Priv file ~p could not be copied to ~p. "
 				      ++"Reason: ~p~n",
diff --git a/lib/common_test/src/ct_run.erl b/lib/common_test/src/ct_run.erl
index fbb9c7ab60..a049ef5695 100644
--- a/lib/common_test/src/ct_run.erl
+++ b/lib/common_test/src/ct_run.erl
@@ -2155,8 +2155,8 @@ continue(_MakeErrors, true) ->
     false;
 continue(_MakeErrors, _AbortIfMissingSuites) ->
     io:nl(),
-    OldGl = group_leader(),
-    case set_group_leader_same_as_shell() of
+    OldGL = group_leader(),
+    case set_group_leader_same_as_shell(OldGL) of
 	true ->
 	    S = self(),
 	    io:format("Failed to compile or locate one "
@@ -2172,7 +2172,7 @@ continue(_MakeErrors, _AbortIfMissingSuites) ->
 					S ! false
 				end
 			end),
-	    group_leader(OldGl, self()),
+	    group_leader(OldGL, self()),
 	    receive R when R==true; R==false ->
 		    R
 	    after 15000 ->
@@ -2184,7 +2184,9 @@ continue(_MakeErrors, _AbortIfMissingSuites) ->
 	    true
     end.
 
-set_group_leader_same_as_shell() ->
+set_group_leader_same_as_shell(OldGL) ->
+    %% find the group leader process on the node in a dirty fashion
+    %% (check initial function call and look in the process dictionary)
     GS2or3 = fun(P) ->
     		     case process_info(P,initial_call) of
     			 {initial_call,{group,server,X}} when X == 2 ; X == 3 ->
@@ -2197,7 +2199,10 @@ set_group_leader_same_as_shell() ->
     	       true == lists:keymember(shell,1,
     				       element(2,process_info(P,dictionary)))] of
     	[GL|_] ->
-    	    group_leader(GL, self());
+            %% check if started from remote node (skip interaction)
+            if node(OldGL) /= node(GL) -> false;
+               true -> group_leader(GL, self())
+            end;
     	[] ->
     	    false
     end.
diff --git a/lib/common_test/src/ct_util.erl b/lib/common_test/src/ct_util.erl
index 82a8743cf0..4d3a2ae7e3 100644
--- a/lib/common_test/src/ct_util.erl
+++ b/lib/common_test/src/ct_util.erl
@@ -188,6 +188,8 @@ do_start(Parent, Mode, LogDir, Verbosity) ->
 	    ok
     end,
 
+    ct_default_gl:start_link(group_leader()),
+
     {StartTime,TestLogDir} = ct_logs:init(Mode, Verbosity),
 
     ct_event:notify(#event{name=test_start,
@@ -474,6 +476,7 @@ loop(Mode,TestData,StartDir) ->
 	    ct_logs:close(Info, StartDir),
 	    ct_event:stop(),
 	    ct_config:stop(),
+	    ct_default_gl:stop(),
 	    ok = file:set_cwd(StartDir),
 	    return(From, Info);
 	{Ref, _Msg} when is_reference(Ref) ->
@@ -926,7 +929,8 @@ warn_duplicates(Suites) ->
 		    [] ->
 			ok;
 		    _ ->
-			io:format(user,"~nWARNING! Deprecated function: ~w:sequences/0.~n"
+			io:format(?def_gl,
+				  "~nWARNING! Deprecated function: ~w:sequences/0.~n"
 				  "         Use group with sequence property instead.~n",[Mod])
 		end
 	end,
@@ -980,12 +984,12 @@ get_profile_data(Profile, Key, StartDir) ->
 	end,
     case Result of
 	{error,enoent} when Profile /= default ->
-	    io:format(user, "~nERROR! Missing profile file ~p~n", [File]),
+	    io:format(?def_gl, "~nERROR! Missing profile file ~p~n", [File]),
 	    undefined;
 	{error,enoent} when Profile == default ->
 	    undefined;
 	{error,Reason} ->
-	    io:format(user,"~nERROR! Error in profile file ~p: ~p~n",
+	    io:format(?def_gl,"~nERROR! Error in profile file ~p: ~p~n",
 		      [WhichFile,Reason]),
 	    undefined;
 	{ok,Data} ->
@@ -995,7 +999,7 @@ get_profile_data(Profile, Key, StartDir) ->
 			_ when is_list(Data) ->
 			    Data;
 			_ ->
-			    io:format(user,
+			    io:format(?def_gl,
 				      "~nERROR! Invalid profile data in ~p~n",
 				      [WhichFile]),
 			    []
@@ -1082,10 +1086,10 @@ open_url(iexplore, Args, URL) ->
 	    Path = proplists:get_value(default, Paths),
 	    [Cmd | _] = string:tokens(Path, "%"),
 	    Cmd1 = Cmd ++ " " ++ Args ++ " " ++ URL,
-	    io:format(user, "~nOpening ~ts with command:~n  ~ts~n", [URL,Cmd1]),
+	    io:format(?def_gl, "~nOpening ~ts with command:~n  ~ts~n", [URL,Cmd1]),
 	    open_port({spawn,Cmd1}, []);
 	_ ->
-	    io:format("~nNo path to iexplore.exe~n",[])
+	    io:format(?def_gl, "~nNo path to iexplore.exe~n",[])
     end,
     win32reg:close(R),
     ok;
@@ -1095,6 +1099,6 @@ open_url(Prog, Args, URL) ->
 		 is_list(Prog) -> Prog
 	      end,
     Cmd = ProgStr ++ " " ++ Args ++ " " ++ URL,
-    io:format(user, "~nOpening ~ts with command:~n  ~ts~n", [URL,Cmd]),
+    io:format(?def_gl, "~nOpening ~ts with command:~n  ~ts~n", [URL,Cmd]),
     open_port({spawn,Cmd},[]),
     ok.
diff --git a/lib/common_test/src/ct_util.hrl b/lib/common_test/src/ct_util.hrl
index d7efa26863..039c8168ec 100644
--- a/lib/common_test/src/ct_util.hrl
+++ b/lib/common_test/src/ct_util.hrl
@@ -23,6 +23,7 @@
 -define(board_table,ct_boards).
 -define(suite_table,ct_suite_data).
 -define(verbosity_table,ct_verbosity_table).
+-define(def_gl, ct_default_gl).
 
 -record(conn, {handle,
 	       targetref,
-- 
cgit v1.2.3