File 2621-eunit-Include-a-stacktrace-when-a-test-times-out.patch of Package erlang

From 5ddb41e42652c270e62aab8baf9bfa1105875362 Mon Sep 17 00:00:00 2001
From: Tomas Abrahamsson <tomas.abrahamsson@gmail.com>
Date: Fri, 3 Sep 2021 17:59:25 +0200
Subject: [PATCH] eunit: Include a stacktrace when a test times out

In an eunit test, when a test case times out, include a stacktrace.
This can be useful when debugging.

In the event of a test timeout, the printout now looks like this:

  1> eunit:test(tl, [verbose]).
  eunit:test(tl, [verbose]).
  ======================== EUnit ========================
  tl: takes_too_long_test (module 'tl')...*timed out*
  in function timer:sleep/1 (timer.erl, line 152)
  in call from tl:takes_too_long_test/0 (/.../tl.erl, line 6)
  in call from eunit_test:'-mf_wrapper/2-fun-0-'/2 (eunit_test.erl, line 273)
  in call from eunit_test:run_testfun/1 (eunit_test.erl, line 71)
  in call from eunit_proc:run_test/1 (/.../eunit_proc.erl, line 528)
  in call from eunit_proc:with_timeout/3 (/.../eunit_proc.erl, line 353)
  in call from eunit_proc:handle_test/2 (/.../eunit_proc.erl, line 511)
  in call from eunit_proc:tests_inorder/3 (/.../eunit_proc.erl, line 453)
  undefined
  =======================================================
    Failed: 0.  Skipped: 0.  Passed: 0.
  One or more tests were cancelled.
  error

Previously, it looked like below.

  1> eunit:test(tl, [verbose]).
  eunit:test(tl, [verbose]).
  ======================== EUnit ========================
  tl: takes_too_long_test (module 'tl')...*timed out*
  undefined
  =======================================================
    Failed: 0.  Skipped: 0.  Passed: 0.
  One or more tests were cancelled.
  error

For an eunit_listener, the handle_cancel/3 will now be called
with {timeout, #{stacktrace => Stacktrace}} as 2nd argument,
in case of a timeout. Previously it was only the atom timeout.

In the eunit_surefire handler, include the stacktrace in the xml,
as well.
---
 lib/eunit/src/eunit_lib.erl      |  2 +-
 lib/eunit/src/eunit_proc.erl     | 11 ++++++++++-
 lib/eunit/src/eunit_surefire.erl |  4 ++++
 lib/eunit/src/eunit_tty.erl      |  3 +++
 lib/eunit/test/Makefile          |  3 ++-
 lib/eunit/test/eunit_SUITE.erl   | 15 +++++++++++++--
 lib/eunit/test/ttimesout.erl     |  6 ++++++
 7 files changed, 39 insertions(+), 5 deletions(-)
 create mode 100644 lib/eunit/test/ttimesout.erl

diff --git a/lib/eunit/src/eunit_lib.erl b/lib/eunit/src/eunit_lib.erl
index 5b7d90ab0d..c7555597c2 100644
--- a/lib/eunit/src/eunit_lib.erl
+++ b/lib/eunit/src/eunit_lib.erl
@@ -36,7 +36,7 @@
 	 command/2, command/3, trie_new/0, trie_store/2, trie_match/2,
 	 split_node/1, consult_file/1, list_dir/1, format_exit_term/1,
 	 format_exception/1, format_exception/2, format_error/1, format_error/2,
-         is_not_test/1]).
+         format_stacktrace/1, is_not_test/1]).
 
 -define(DEFAULT_DEPTH, 20).
 
diff --git a/lib/eunit/src/eunit_proc.erl b/lib/eunit/src/eunit_proc.erl
index fb6619a6d4..7622bab2a9 100644
--- a/lib/eunit/src/eunit_proc.erl
+++ b/lib/eunit/src/eunit_proc.erl
@@ -95,6 +95,9 @@ get_output() ->
 %%   {cancel, Descriptor}
 %%       where Descriptor can be:
 %%           timeout            a timeout occurred
+%%           {timeout, #{stacktrace := Stacktrace}
+%%                              a timeout occurred and there is a stacktrace
+%%                              of the eunit test
 %%           {blame, Id}        forced to terminate because of item `Id'
 %%           {abort, Cause}     the test or group failed to execute
 %%           {exit, Reason}     the test process terminated unexpectedly
@@ -273,7 +276,13 @@ insulator_wait(Child, Parent, Buf, St) ->
 	    io_request(From, ReplyAs, Req, []),
 	    insulator_wait(Child, Parent, Buf, St);
 	{timeout, Child, Id} ->
-	    exit_messages(Id, timeout, St),
+	    Timeout = case process_info(Child, current_stacktrace) of
+			  undefined ->
+			      timeout;
+			  {current_stacktrace, Stack} ->
+			      {timeout, #{stacktrace => Stack}}
+		      end,
+	    exit_messages(Id, Timeout, St),
 	    kill_task(Child, St);
 	{'EXIT', Child, normal} ->
 	    terminate_insulator(St);
diff --git a/lib/eunit/src/eunit_surefire.erl b/lib/eunit/src/eunit_surefire.erl
index 71f3765f21..a37ec72965 100644
--- a/lib/eunit/src/eunit_surefire.erl
+++ b/lib/eunit/src/eunit_surefire.erl
@@ -395,6 +395,10 @@ format_testcase_result({skipped, {abort, Error}}) when is_tuple(Error) ->
     [?INDENT, ?INDENT, <<"<skipped type=\"">>, escape_attr(atom_to_list(element(1, Error))), <<"\">">>, ?NEWLINE,
     escape_text(eunit_lib:format_error(Error)),
     ?INDENT, ?INDENT, <<"</skipped>">>, ?NEWLINE];
+format_testcase_result({skipped, {timeout, #{stacktrace := Stack}}}) ->
+    [?INDENT, ?INDENT, <<"<skipped type=\"timeout\">">>, ?NEWLINE,
+    escape_text(eunit_lib:format_stacktrace(Stack)),
+    ?INDENT, ?INDENT, <<"</skipped>">>, ?NEWLINE];
 format_testcase_result({skipped, {Type, Term}}) when is_atom(Type) ->
     [?INDENT, ?INDENT, <<"<skipped type=\"">>, escape_attr(atom_to_list(Type)), <<"\">">>, ?NEWLINE,
     escape_text(io_lib:write(Term)),
diff --git a/lib/eunit/src/eunit_tty.erl b/lib/eunit/src/eunit_tty.erl
index fd5245c971..aee8e466e2 100644
--- a/lib/eunit/src/eunit_tty.erl
+++ b/lib/eunit/src/eunit_tty.erl
@@ -253,6 +253,9 @@ format_cancel(undefined, _) ->
     "*skipped*\n";
 format_cancel(timeout, _) ->
     "*timed out*\n";
+format_cancel({timeout, #{stacktrace := Stack}}, _) ->
+    ["*timed out*\n",
+     eunit_lib:format_stacktrace(Stack)];
 format_cancel({startup, Reason}, Depth) ->
     io_lib:fwrite("*could not start test process*\n::~tP\n\n",
 		  [Reason, Depth]);
diff --git a/lib/eunit/test/Makefile b/lib/eunit/test/Makefile
index 99d1f8093a..903adfde3f 100644
--- a/lib/eunit/test/Makefile
+++ b/lib/eunit/test/Makefile
@@ -24,7 +24,8 @@ MODULES =  \
 	eunit_SUITE \
 	tc0 \
 	tlatin \
-	tutf8
+	tutf8 \
+	ttimesout
 
 ERL_FILES= $(MODULES:%=%.erl)
 
diff --git a/lib/eunit/test/eunit_SUITE.erl b/lib/eunit/test/eunit_SUITE.erl
index df754e46c5..5940fd3857 100644
--- a/lib/eunit/test/eunit_SUITE.erl
+++ b/lib/eunit/test/eunit_SUITE.erl
@@ -22,7 +22,8 @@
 -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
 	 init_per_group/2,end_per_group/2,
 	 app_test/1,appup_test/1,eunit_test/1,surefire_utf8_test/1,surefire_latin_test/1,
-	 surefire_c0_test/1, surefire_ensure_dir_test/1]).
+	 surefire_c0_test/1, surefire_ensure_dir_test/1,
+	 stacktrace_at_timeout_test/1]).
 
 -include_lib("common_test/include/ct.hrl").
 
@@ -30,7 +31,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
 
 all() ->
     [app_test, appup_test, eunit_test, surefire_utf8_test, surefire_latin_test,
-     surefire_c0_test, surefire_ensure_dir_test].
+     surefire_c0_test, surefire_ensure_dir_test, stacktrace_at_timeout_test].
 
 groups() ->
     [].
@@ -81,6 +82,16 @@ surefire_ensure_dir_test(Config) when is_list(Config) ->
     ok = eunit:test(tc0, [{report,{eunit_surefire,[{dir,XMLDir}]}}]),
     ok = file:del_dir_r(XMLDir).
 
+stacktrace_at_timeout_test(Config) when is_list(Config) ->
+    Chars = check_surefire(ttimesout),
+    case string:find(Chars, "in call from") of
+        nomatch ->
+            ct:pal("Surefire XML:~n~ts", [Chars]),
+            ct:fail(missing_stacktrace_in_surefire);
+        _ ->
+            ok
+    end.
+
 check_surefire(Module) ->
 	File = "TEST-"++atom_to_list(Module)++".xml",
 	file:delete(File),
diff --git a/lib/eunit/test/ttimesout.erl b/lib/eunit/test/ttimesout.erl
new file mode 100644
index 0000000000..91f6be847d
--- /dev/null
+++ b/lib/eunit/test/ttimesout.erl
@@ -0,0 +1,6 @@
+-module(ttimesout).
+
+-include_lib("eunit/include/eunit.hrl").
+
+times_out_test_() ->
+    {timeout, 1, fun() -> timer:sleep(20_000) end}.
-- 
2.31.1

openSUSE Build Service is sponsored by