From d7101fd2c2ce8bf609670043585c57010a3707c6 Mon Sep 17 00:00:00 2001
From: Siri Hansen <siri@erlang.org>
Date: Tue, 14 Mar 2017 11:18:46 +0100
Subject: [ct] Add 'keep_logs' option

If setting the value for this option to an integer, N, common_test
will remove all ct_run.* directories in the current log directory,
except the N newest.

The default value for the 'keep_logs' option is 'all', which means that
no logs will be deleted.

'keep_logs' can be used in combination with refresh_logs, or in a normal
common_test test run.
---
 lib/common_test/doc/src/ct.xml                     |   3 +-
 lib/common_test/doc/src/ct_run.xml                 |   8 +-
 lib/common_test/doc/src/run_test_chapter.xml       |  12 ++
 lib/common_test/src/ct_logs.erl                    |  37 +++-
 lib/common_test/src/ct_run.erl                     |  18 ++
 lib/common_test/test/Makefile                      |   3 +-
 lib/common_test/test/ct_keep_logs_SUITE.erl        | 186 +++++++++++++++++++++
 .../ct_keep_logs_SUITE_data/keep_logs_SUITE.erl    |  32 ++++
 lib/common_test/test/ct_test_support.erl           |   3 +
 9 files changed, 297 insertions(+), 5 deletions(-)
 create mode 100644 lib/common_test/test/ct_keep_logs_SUITE.erl
 create mode 100644 lib/common_test/test/ct_keep_logs_SUITE_data/keep_logs_SUITE.erl

(limited to 'lib')

diff --git a/lib/common_test/doc/src/ct.xml b/lib/common_test/doc/src/ct.xml
index ea9f956271..cd1e51a9a9 100644
--- a/lib/common_test/doc/src/ct.xml
+++ b/lib/common_test/doc/src/ct.xml
@@ -1190,7 +1190,7 @@
         Opts.</fsummary>
       <type>
         <v>Opts = [OptTuples]</v>
-        <v>OptTuples = {dir, TestDirs} | {suite, Suites} | {group, Groups} | {testcase, Cases} | {spec, TestSpecs} | {join_specs, Bool} | {label, Label} | {config, CfgFiles} | {userconfig, UserConfig} | {allow_user_terms, Bool} | {logdir, LogDir} | {silent_connections, Conns} | {stylesheet, CSSFile} | {cover, CoverSpecFile} | {cover_stop, Bool} | {step, StepOpts} | {event_handler, EventHandlers} | {include, InclDirs} | {auto_compile, Bool} | {abort_if_missing_suites, Bool} | {create_priv_dir, CreatePrivDir} | {multiply_timetraps, M} | {scale_timetraps, Bool} | {repeat, N} | {duration, DurTime} | {until, StopTime} | {force_stop, ForceStop} | {decrypt, DecryptKeyOrFile} | {refresh_logs, LogDir} | {logopts, LogOpts} | {verbosity, VLevels} | {basic_html, Bool} | {esc_chars, Bool} | {ct_hooks, CTHs} | {enable_builtin_hooks, Bool} | {release_shell, Bool}</v>
+        <v>OptTuples = {dir, TestDirs} | {suite, Suites} | {group, Groups} | {testcase, Cases} | {spec, TestSpecs} | {join_specs, Bool} | {label, Label} | {config, CfgFiles} | {userconfig, UserConfig} | {allow_user_terms, Bool} | {logdir, LogDir} | {silent_connections, Conns} | {stylesheet, CSSFile} | {cover, CoverSpecFile} | {cover_stop, Bool} | {step, StepOpts} | {event_handler, EventHandlers} | {include, InclDirs} | {auto_compile, Bool} | {abort_if_missing_suites, Bool} | {create_priv_dir, CreatePrivDir} | {multiply_timetraps, M} | {scale_timetraps, Bool} | {repeat, N} | {duration, DurTime} | {until, StopTime} | {force_stop, ForceStop} | {decrypt, DecryptKeyOrFile} | {refresh_logs, LogDir} | {logopts, LogOpts} | {verbosity, VLevels} | {basic_html, Bool} | {esc_chars, Bool} | {keep_logs,KeepSpec} | {ct_hooks, CTHs} | {enable_builtin_hooks, Bool} | {release_shell, Bool}</v>
         <v>TestDirs = [string()] | string()</v>
         <v>Suites = [string()] | [atom()] | string() | atom()</v>
         <v>Cases = [atom()] | atom()</v>
@@ -1226,6 +1226,7 @@
         <v>VLevels = VLevel | [{Category, VLevel}]</v>
         <v>VLevel = integer()</v>
         <v>Category = atom()</v>
+	<v>KeepSpec = all | pos_integer()</v>
         <v>CTHs = [CTHModule | {CTHModule, CTHInitArgs}]</v>
         <v>CTHModule = atom()</v>
         <v>CTHInitArgs = term()</v>
diff --git a/lib/common_test/doc/src/ct_run.xml b/lib/common_test/doc/src/ct_run.xml
index 9e6229f1dd..af30550719 100644
--- a/lib/common_test/doc/src/ct_run.xml
+++ b/lib/common_test/doc/src/ct_run.xml
@@ -125,6 +125,7 @@
   [-until [YYMoMoDD]HHMMSS [-force_stop [skip_rest]]]
   [-basic_html]
   [-no_esc_chars]
+  [-keep_logs all | NLogs]
   [-ct_hooks CTHModule1 CTHOpts1 and CTHModule2 CTHOpts2 and ..
    CTHModuleN CTHOptsN]
   [-exit_status ignore_config]
@@ -164,6 +165,7 @@
   [-until [YYMoMoDD]HHMMSS [-force_stop [skip_rest]]]
   [-basic_html]
   [-no_esc_chars]
+  [-keep_logs all | NLogs]
   [-ct_hooks CTHModule1 CTHOpts1 and CTHModule2 CTHOpts2 and ..
    CTHModuleN CTHOptsN]
   [-exit_status ignore_config]</pre>
@@ -189,13 +191,15 @@
   [-scale_timetraps]
   [-create_priv_dir auto_per_run | auto_per_tc | manual_per_tc]
   [-basic_html]
-  [-no_esc_chars]</pre>
+  [-no_esc_chars]
+  [-keep_logs all | NLogs]</pre>
   </section>
 
   <section>
     <title>Refresh HTML Index Files</title>
     <pre>
- ct_run -refresh_logs [-logdir LogDir] [-basic_html]</pre>
+ ct_run -refresh_logs [-logdir LogDir] [-basic_html]
+  [-keep_logs all | NLogs]</pre>
   </section>
 
   <section>
diff --git a/lib/common_test/doc/src/run_test_chapter.xml b/lib/common_test/doc/src/run_test_chapter.xml
index 76e306c4ed..aea4d05867 100644
--- a/lib/common_test/doc/src/run_test_chapter.xml
+++ b/lib/common_test/doc/src/run_test_chapter.xml
@@ -1353,6 +1353,18 @@
 	  only works when <c>Common Test</c> is running. Printouts with <c>ct:pal/2</c>
 	  are however always displayed on screen.</p></note>
 	</section>
+
+	<section>
+	  <marker id="delete_old_logs"></marker>
+	  <title>Delete Old Logs</title>
+	  <p><c>Common Test</c> can automatically delete old log. This
+	    is specified with the <c>keep_logs</c> option. The default
+	    value for this option is <c>all</c>, which means that no
+	    logs are deleted. If the value is set to an
+	    integer, <c>N</c>, <c>Common Test</c> deletes
+	    all <c>ct_run.&lt;timestamp&gt;</c> directories, except
+	    the <c>N</c> newest.</p>
+	</section>
       </section>
   
       <section>
diff --git a/lib/common_test/src/ct_logs.erl b/lib/common_test/src/ct_logs.erl
index 09ad709da5..544b019b64 100644
--- a/lib/common_test/src/ct_logs.erl
+++ b/lib/common_test/src/ct_logs.erl
@@ -41,6 +41,7 @@
 -export([xhtml/2, locate_priv_file/1, make_relative/1]).
 -export([insert_javascript/1]).
 -export([uri/1]).
+-export([parse_keep_logs/1]).
 
 %% Logging stuff directly from testcase
 -export([tc_log/3, tc_log/4, tc_log/5, tc_log/6,
@@ -1946,7 +1947,11 @@ make_all_runs_index(When) ->
 	end,	
 
     Dirs = filelib:wildcard(logdir_prefix()++"*.*"),
-    DirsSorted = (catch sort_all_runs(Dirs)),
+    DirsSorted0 = (catch sort_all_runs(Dirs)),
+    DirsSorted =
+        if When == start -> DirsSorted0;
+           true -> maybe_delete_old_dirs(DirsSorted0)
+        end,
 
     LogCacheInfo = get_cache_data(UseCache),
 	
@@ -2064,6 +2069,36 @@ sort_ct_runs(Dirs) ->
 	      {DateHH1,MM1,SS1} =< {DateHH2,MM2,SS2}
       end, Dirs).
 
+parse_keep_logs([Str="all"]) ->
+    parse_keep_logs(list_to_atom(Str));
+parse_keep_logs([NStr]) ->
+    parse_keep_logs(list_to_integer(NStr));
+parse_keep_logs(all) ->
+    all;
+parse_keep_logs(N) when is_integer(N), N>0 ->
+    N.
+
+maybe_delete_old_dirs(Sorted) ->
+    {Keep,Delete} =
+        case application:get_env(common_test, keep_logs) of
+            {ok,MaxN} when is_integer(MaxN), length(Sorted)>MaxN ->
+                lists:split(MaxN,Sorted);
+            _ ->
+                {Sorted,[]}
+        end,
+    delete_old_dirs(Delete),
+    Keep.
+
+delete_old_dirs([]) ->
+    ok;
+delete_old_dirs(Dirs) ->
+    io:put_chars("\n  Removing old test directories:\n"),
+    [begin
+         io:put_chars("    " ++ Dir ++ "\n"),
+         rm_dir(Dir)
+     end|| Dir <- Dirs],
+    ok.
+
 dir_diff_all_runs(Dirs, LogCache) ->
     case LogCache#log_cache.all_runs of
 	[] ->
diff --git a/lib/common_test/src/ct_run.erl b/lib/common_test/src/ct_run.erl
index cac176de3a..ba27303c81 100644
--- a/lib/common_test/src/ct_run.erl
+++ b/lib/common_test/src/ct_run.erl
@@ -363,6 +363,12 @@ script_start1(Parent, Args) ->
 	_ ->
 	    application:set_env(common_test, disable_log_cache, true)
     end,
+    %% log_cleanup - used by ct_logs
+    KeepLogs = get_start_opt(keep_logs,
+                             fun ct_logs:parse_keep_logs/1,
+                             all,
+                             Args),
+    application:set_env(common_test, keep_logs, KeepLogs),
 
     Opts = #opts{label = Label, profile = Profile,
 		 vts = Vts, shell = Shell,
@@ -970,6 +976,12 @@ run_test1(StartOpts) when is_list(StartOpts) ->
 	    stop_trace(Tracing),
 	    exit(Res);
 	RefreshDir ->
+            %% log_cleanup - used by ct_logs
+            KeepLogs = get_start_opt(keep_logs,
+                                     fun ct_logs:parse_keep_logs/1,
+                                     all,
+                                     StartOpts),
+            application:set_env(common_test, keep_logs, KeepLogs),
 	    ok = refresh_logs(?abs(RefreshDir)),
 	    exit(done)
     end.
@@ -1131,6 +1143,12 @@ run_test2(StartOpts) ->
 	DisableCacheBool ->
 	    application:set_env(common_test, disable_log_cache, DisableCacheBool)
     end,
+    %% log_cleanup - used by ct_logs
+    KeepLogs = get_start_opt(keep_logs,
+                             fun ct_logs:parse_keep_logs/1,
+                             all,
+                             StartOpts),
+    application:set_env(common_test, keep_logs, KeepLogs),
 
     %% stepped execution
     Step = get_start_opt(step, value, StartOpts),
diff --git a/lib/common_test/test/Makefile b/lib/common_test/test/Makefile
index 2f0fc2e05a..b8fb678dbd 100644
--- a/lib/common_test/test/Makefile
+++ b/lib/common_test/test/Makefile
@@ -71,7 +71,8 @@ MODULES= \
 	test_server_test_lib \
 	ct_release_test_SUITE \
 	ct_log_SUITE \
-        ct_SUITE
+        ct_SUITE \
+	ct_keep_logs_SUITE
 
 ERL_FILES= $(MODULES:%=%.erl)
 HRL_FILES= test_server_test_lib.hrl
diff --git a/lib/common_test/test/ct_keep_logs_SUITE.erl b/lib/common_test/test/ct_keep_logs_SUITE.erl
new file mode 100644
index 0000000000..34bfaaa97a
--- /dev/null
+++ b/lib/common_test/test/ct_keep_logs_SUITE.erl
@@ -0,0 +1,186 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2009-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%
+%%
+
+%%%-------------------------------------------------------------------
+%%% File: ct_keep_logs_SUITE
+%%%
+%%% Description:
+%%% Test the 'keep_logs' option
+%%%
+%%%-------------------------------------------------------------------
+-module(ct_keep_logs_SUITE).
+
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+-include_lib("common_test/include/ct_event.hrl").
+
+-define(eh, ct_test_support_eh).
+
+%%--------------------------------------------------------------------
+%% TEST SERVER CALLBACK FUNCTIONS
+%%--------------------------------------------------------------------
+
+init_per_suite(Config0) ->
+    ct_test_support:init_per_suite(Config0).
+
+end_per_suite(Config) ->
+    ct_test_support:end_per_suite(Config).
+
+init_per_testcase(TestCase, Config) ->
+    ct_test_support:init_per_testcase(TestCase, Config).
+
+end_per_testcase(TestCase, Config) ->
+    ct_test_support:end_per_testcase(TestCase, Config).
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+    [
+     keep_logs,
+     refresh_logs
+    ].
+
+%%--------------------------------------------------------------------
+%% TEST CASES
+%%--------------------------------------------------------------------
+
+%% Test the keep_logs option with normal common_test runs
+keep_logs(Config) ->
+    DataDir = ?config(data_dir, Config),
+    Suite = filename:join(DataDir, "keep_logs_SUITE"),
+    Opts0 = ct_test_support:get_opts(Config),
+    Opts = [{suite,Suite},{label,keep_logs} | Opts0],
+
+    LogDir=?config(logdir,Opts),
+    KeepLogsDir = filename:join(LogDir,unique_name("keep_logs-")),
+    ok = file:make_dir(KeepLogsDir),
+    Opts1 = lists:keyreplace(logdir,1,Opts,{logdir,KeepLogsDir}),
+    ct:log("New LogDir = ~s", [KeepLogsDir]),
+
+    %% Create 6 ct_run.* log directories
+    [ok = ct_test_support:run(Opts1, Config) || _ <- lists:seq(1,3)],
+
+    %% Verify the number of directories
+    WC = filename:join(KeepLogsDir,"ct_run.ct@*"),
+    L1 = filelib:wildcard(WC),
+    6 = length(L1),
+
+    %% Keep all logs
+    {1,0,{0,0}}=ct_test_support:run_ct_run_test([{keep_logs,all}|Opts1], Config),
+    L2 = filelib:wildcard(WC),
+    7 = length(L2),
+    0 = ct_test_support:run_ct_script_start([{keep_logs,all}|Opts1], Config),
+    L3 = filelib:wildcard(WC),
+    8 = length(L3),
+
+    %% N<length of list
+    {1,0,{0,0}}=ct_test_support:run_ct_run_test([{keep_logs,7}|Opts1], Config),
+    L4 = filelib:wildcard(WC),
+    7 = length(L4),
+    0 = ct_test_support:run_ct_script_start([{keep_logs,6}|Opts1], Config),
+    L5 = filelib:wildcard(WC),
+    6 = length(L5),
+
+    %% N>length of list
+    {1,0,{0,0}}=ct_test_support:run_ct_run_test([{keep_logs,10}|Opts1], Config),
+    L6 = filelib:wildcard(WC),
+    7 = length(L6),
+    0 = ct_test_support:run_ct_script_start([{keep_logs,10}|Opts1], Config),
+    L7 = filelib:wildcard(WC),
+    8 = length(L7),
+
+    %% N==length of list
+    {1,0,{0,0}}=ct_test_support:run_ct_run_test([{keep_logs,8}|Opts1], Config),
+    L8 = filelib:wildcard(WC),
+    8 = length(L8),
+    0 = ct_test_support:run_ct_script_start([{keep_logs,8}|Opts1], Config),
+    L9 = filelib:wildcard(WC),
+    8 = length(L9),
+
+    %% N==length of list + current run
+    {1,0,{0,0}}=ct_test_support:run_ct_run_test([{keep_logs,9}|Opts1], Config),
+    L10 = filelib:wildcard(WC),
+    9 = length(L10),
+    0 = ct_test_support:run_ct_script_start([{keep_logs,10}|Opts1], Config),
+    L11 = filelib:wildcard(WC),
+    10 = length(L11).
+
+%% Test the keep_logs option togwther with the refresh_logs option
+refresh_logs(Config) ->
+    DataDir = ?config(data_dir, Config),
+    Suite = filename:join(DataDir, "keep_logs_SUITE"),
+    Opts0 = ct_test_support:get_opts(Config),
+    LogDir=?config(logdir,Opts0),
+    KeepLogsDir = filename:join(LogDir,unique_name("refresh_logs-")),
+    ok = file:make_dir(KeepLogsDir),
+    Opts1 = lists:keyreplace(logdir,1,Opts0,{logdir,KeepLogsDir}),
+    ct:log("New LogDir = ~s", [KeepLogsDir]),
+
+    %% Create 6 ct_run.* log directories
+    SuiteOpts = [{suite,Suite},{label,refresh_logs} | Opts1],
+    [ok = ct_test_support:run(SuiteOpts, Config) || _ <- lists:seq(1,3)],
+
+    %% Verify the number of directories
+    WC = filename:join(KeepLogsDir,"ct_run.ct@*"),
+    L1 = filelib:wildcard(WC),
+    6 = length(L1),
+
+    RefreshOpts =  [{refresh_logs,KeepLogsDir},{label,refresh_logs} | Opts1],
+
+    %% Keep all logs (note that refresh_logs option prevents the
+    %% creation of a new log directory for the current run)
+    done = ct_test_support:run_ct_run_test([{keep_logs,all}|RefreshOpts], Config),
+    L2 = filelib:wildcard(WC),
+    6 = length(L2),
+    0 = ct_test_support:run_ct_script_start([{keep_logs,all}|RefreshOpts],Config),
+    L3 = filelib:wildcard(WC),
+    6 = length(L3),
+
+    %% N<length of list
+    done = ct_test_support:run_ct_run_test([{keep_logs,5}|RefreshOpts], Config),
+    L5 = filelib:wildcard(WC),
+    5 = length(L5),
+    0 = ct_test_support:run_ct_script_start([{keep_logs,4}|RefreshOpts], Config),
+    L6 = filelib:wildcard(WC),
+    4 = length(L6),
+
+    %% N>length of list
+    done = ct_test_support:run_ct_run_test([{keep_logs,5}|RefreshOpts], Config),
+    L7 = filelib:wildcard(WC),
+    4 = length(L7),
+    0 = ct_test_support:run_ct_script_start([{keep_logs,5}|RefreshOpts], Config),
+    L8 = filelib:wildcard(WC),
+    4 = length(L8),
+
+    %% N==length of list
+    done = ct_test_support:run_ct_run_test([{keep_logs,4}|RefreshOpts], Config),
+    L9 = filelib:wildcard(WC),
+    4 = length(L9),
+    0 = ct_test_support:run_ct_script_start([{keep_logs,4}|RefreshOpts], Config),
+    L10 = filelib:wildcard(WC),
+    4 = length(L10),
+
+    ok.
+%%%-----------------------------------------------------------------
+%%% Internal
+unique_name(Prefix) ->
+    I = erlang:unique_integer([positive]),
+    Prefix ++ integer_to_list(I).
diff --git a/lib/common_test/test/ct_keep_logs_SUITE_data/keep_logs_SUITE.erl b/lib/common_test/test/ct_keep_logs_SUITE_data/keep_logs_SUITE.erl
new file mode 100644
index 0000000000..2bccae564b
--- /dev/null
+++ b/lib/common_test/test/ct_keep_logs_SUITE_data/keep_logs_SUITE.erl
@@ -0,0 +1,32 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2009-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(keep_logs_SUITE).
+
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+
+suite() ->
+    [].
+all() ->
+    [test_case].
+
+test_case(_Config) ->
+    ok.
diff --git a/lib/common_test/test/ct_test_support.erl b/lib/common_test/test/ct_test_support.erl
index 05a452b99d..06d591871f 100644
--- a/lib/common_test/test/ct_test_support.erl
+++ b/lib/common_test/test/ct_test_support.erl
@@ -351,6 +351,9 @@ check_result(CtRunTestResult,ExitStatus,Opts)
     catch _:_ ->
 	    {error,{unexpected_return_value,{CtRunTestResult,ExitStatus}}}
     end;
+check_result(done,0,_Opts) ->
+    %% refresh_logs return
+    ok;
 check_result(CtRunTestResult,ExitStatus,_Opts) ->
     {error,{unexpected_return_value,{CtRunTestResult,ExitStatus}}}.
 
-- 
cgit v1.2.3