From 044f768b6545234461f173e8a959379630723e8f Mon Sep 17 00:00:00 2001
From: Lukas Larsson
The
The
Other tuples than the ones defined will simply be ignored.
diff --git a/lib/common_test/doc/src/ct_hooks.xml b/lib/common_test/doc/src/ct_hooks.xml new file mode 100644 index 0000000000..0d59ce3b22 --- /dev/null +++ b/lib/common_test/doc/src/ct_hooks.xml @@ -0,0 +1,556 @@ + + + + +This feature is in alpha release right now. This means that the + interface may change in the future and that there may be bugs. We + encourage you to use this feature, but be prepared + that there might be bugs and that the interface might change + inbetween releases.
The Common Test Hook (henceforth called CTH) framework allows + extensions of the default behaviour of Common Test by means of callbacks + before and after all test suite calls. It is meant for advanced users of + Common Test which want to abstract out behaviour which is common to + multiple test suites.
+ +In brief, Common Test Hooks allows you to:
+ +The following sections describe the mandatory and optional CTH
+ functions Common Test will call during test execution. For more details
+ see
For information about how to add a CTH to your suite see
+
See the
+
The following functions define the callback interface + for a Common Test Hook.
+MANDATORY
+ +Always called before any other callback function. + Use this to initiate any common state. + It should return a state for this CTH.
+ +For details about when init is called see
+
OPTIONAL
+ +This function is called before
+
Note that this function is only called if the CTH has been added
+ before init_per_suite is run, see
+
OPTIONAL
+ +This function is called after
+
Note that this function is only called if the CTH has been added
+ before or in init_per_suite, see
+
OPTIONAL
+ +This function is called before
+
OPTIONAL
+ +This function is called after
+
OPTIONAL
+ +This function is called before
+
Note that it is not possible to add CTH's here right now, + that feature might be added later, + but it would right now break backwards compatability.
+OPTIONAL
+ +This function is called after
+
OPTIONAL
+ +This function is called before
+
OPTIONAL
+ +This function is called after
+
OPTIONAL
+ +This function is called before
+
OPTIONAL
+ +This function is called after
+
OPTIONAL
+ +This function is called whenever a testcase fails.
+ It is called after the post function has been called for
+ the testcase which failed. i.e.
+ if init_per_suite fails this function is called after
+
The data which comes with the Reason follows the same format as the
+
OPTIONAL
+ +This function is called whenever a testcase is skipped.
+ It is called after the post function has been called for the
+ testcase which was skipped.
+ i.e. if init_per_group is skipped this function is called after
+
The data which comes with the Reason follows the same format as
+
OPTIONAL
+ +This function is called at the end of a CTH's
+
OPTIONAL
+ +The
This function should NOT have any side effects as it might + be called multiple times by Common Test.
+ +If not implemented the CTH will act as if this function returned a
+ call to
This feature is in alpha release right now. This means that the + interface may change in the future and that there may be bugs. We + encourage you to use this feature, but be prepared + that there might be bugs and that the interface might change + inbetween releases.
+ The Common Test Hook (henceforth called CTH) framework allows + extensions of the default behaviour of Common Test by means of hooks + before and after all test suite calls. CTHs allow advanced Common Test + users to abstract out behaviour which is common to multiple test suites + without littering all test suites with library calls. Some example + usages are: logging, starting and monitoring external systems, + building C files needed by the tests and much more!
+ +In brief, Common Test Hooks allows you to:
+ +The following sections describe how to use CTHs, when they are run + and how to manipulate your test results in a CTH
+ +When executing within a CTH all timetraps are shutoff. So + if your CTH never returns, the entire test run will be stalled!
+There are multiple ways to install a CTH in your test run. You can do it + for all tests in a run, for specific test suites and for specific groups + within a test suite. If you want a CTH to be present in all test suites + within your test run there are three different ways to accomplish that. +
+ +You can also add CTHs within a test suite. This is done by returning
+
By default each installation of a CTH will cause a new instance of it
+ to be activated. This can cause problems if you want to be able to
+ override CTHs in test specifications while still having them in the
+ suite info function. The
+
Once the CTH is installed into a certain test run it will be there until
+ its scope is expired. The scope of a CTH depends on when it is
+ installed.
+ The
CTHs are run with the same process scoping as normal test suites + i.e. a different process will execute the init_per_suite hooks then the + init_per_group or per_testcase hooks. So if you want to spawn a + process in the CTH you cannot link with the CTH process as it will exit + after the post hook ends. Also if you for some reason need an ETS + table with your CTH, you will have to spawn a process which handles + it.
+It is through CTHs possible to manipulate the results of tests and + configuration functions. The main purpose of doing this with CTHs is to + allow common patterns to be abstracted out from test test suites and applied to + multiple test suites without duplicating any code. All of the callback + functions for a CTH follow a common interface, this interface is + described below.
+ +It is only possible to hook into test function which exists in the test
+ suite. So in order for a CTH to hook in before
+
+ It is possible in a CTH to hook in behaviour before
+
pre_init_per_suite(SuiteName, Config, CTHState) ->
+ case db:connect() of
+ {error,_Reason} ->
+ {{fail, "Could not connect to DB"}, CTHState};
+ {ok, Handle} ->
+ {[{db_handle, Handle} | Config], CTHState#state{ handle = Handle }}
+ end.
+
+ It is also possible in a CTH to hook in behaviour after
+
The return value of the CTH function is always a combination of an
+ result for the suite/group/test and an updated
post_end_per_testcase(_TC, Config, {'EXIT',{_,_}}, CTHState) ->
+ case db:check_consistency() of
+ true ->
+ %% DB is good, pass the test.
+ {proplists:delete(tc_status, Config), CTHState};
+ false ->
+ %% DB is not good, mark as skipped instead of failing
+ {{skip, "DB is inconsisten!"}, CTHState}
+ end;
+post_end_per_testcase(_TC, Config, Return, CTHState) ->
+ %% Do nothing if tc does not crash.
+ {Return, CTHState}.
+
+
+ After any post hook has been executed for all installed CTHs,
+
The CTH below will log information about a test run into a format
+ parseable by
%%% @doc Common Test Example Common Test Hook module.
+-module(example_cth).
+
+%% Callbacks
+-export([id/1]).
+-export([init/2]).
+
+-export([pre_init_per_suite/3]).
+-export([post_init_per_suite/4]).
+-export([pre_end_per_suite/3]).
+-export([post_end_per_suite/4]).
+
+-export([pre_init_per_group/3]).
+-export([post_init_per_group/4]).
+-export([pre_end_per_group/3]).
+-export([post_end_per_group/4]).
+
+-export([pre_init_per_testcase/3]).
+-export([post_end_per_testcase/4]).
+
+-export([on_tc_fail/3]).
+-export([on_tc_skip/3]).
+
+-export([terminate/1]).
+
+-record(state, { file_handle, total, suite_total, ts, tcs, data }).
+
+%% @doc Return a unique id for this CTH.
+id(Opts) ->
+ proplists:get_value(filename, Opts, "/tmp/file.log").
+
+%% @doc Always called before any other callback function. Use this to initiate
+%% any common state.
+init(Id, Opts) ->
+ {ok,D} = file:open(Id,[write]),
+ #state{ file_handle = D, total = 0, data = [] }.
+
+%% @doc Called before init_per_suite is called.
+pre_init_per_suite(Suite,Config,State) ->
+ {Config, State#state{ suite_total = 0, tcs = [] }}.
+
+%% @doc Called after init_per_suite.
+post_init_per_suite(Suite,Config,Return,State) ->
+ {Return, State}.
+
+%% @doc Called before end_per_suite.
+pre_end_per_suite(Suite,Config,State) ->
+ {Config, State}.
+
+%% @doc Called after end_per_suite.
+post_end_per_suite(Suite,Config,Return,State) ->
+ Data = {suites, Suite, State#state.suite_total, lists:reverse(State#state.tcs)},
+ {Return, State#state{ data = [Data | State#state.data] ,
+ total = State#state.total + State#state.suite_total } }.
+
+%% @doc Called before each init_per_group.
+pre_init_per_group(Group,Config,State) ->
+ {Config, State}.
+
+%% @doc Called after each init_per_group.
+post_init_per_group(Group,Config,Return,State) ->
+ {Return, State}.
+
+%% @doc Called after each end_per_group.
+pre_end_per_group(Group,Config,State) ->
+ {Config, State}.
+
+%% @doc Called after each end_per_group.
+post_end_per_group(Group,Config,Return,State) ->
+ {Return, State}.
+
+%% @doc Called before each test case.
+pre_init_per_testcase(TC,Config,State) ->
+ {Config, State#state{ ts = now(), total = State#state.suite_total + 1 } }.
+
+%% @doc Called after each test case.
+post_end_per_testcase(TC,Config,Return,State) ->
+ TCInfo = {testcase, TC, Return, timer:now_diff(now(), State#state.ts)},
+ {Return, State#state{ ts = undefined, tcs = [TCInfo | State#state.tcs] } }.
+
+%% @doc Called after post_init_per_suite, post_end_per_suite, post_init_per_group,
+%% post_end_per_group and post_end_per_testcase if the suite, group or test case failed.
+on_tc_fail(TC, Reason, State) ->
+ State.
+
+%% @doc Called when a test case is skipped by either user action
+%% or due to an init function failing.
+on_tc_skip(TC, Reason, State) ->
+ State.
+
+%% @doc Called when the scope of the CTH is done
+terminate(State) ->
+ io:format(State#state.file_handle, "~p.~n",
+ [{test_run, State#state.total, State#state.data}]),
+ file:close(State#state.file_handle),
+ ok.
+ This feature is in alpha release right now. This means that the - interface may change in the future and that there may be bugs. We - encourage you to use this feature, but be prepared - that there might be bugs and that the interface might change - inbetween releases.
The Suite Callback (henceforth called SCB) framework allows - extensions of the default behaviour of Common Test by means of callbacks - before and after all test suite calls. It is meant for advanced users of - Common Test which want to abstract out behaviour which is common to - multiple test suites.
- -In brief, Suite Callbacks allows you to:
- -The following sections describe the mandatory and optional SCB
- functions Common Test will call during test execution. For more details
- see
For information about how to add a SCB to your suite see
-
See the
-
The following functions define the callback interface - for a suite callback.
-MANDATORY
- -Always called before any other callback function. - Use this to initiate any common state. - It should return a state for this SCB.
- -For details about when init is called see
-
OPTIONAL
- -This function is called before
-
Note that this function is only called if the SCB has been added
- before init_per_suite is run, see
-
OPTIONAL
- -This function is called after
-
Note that this function is only called if the SCB has been added
- before or in init_per_suite, see
-
OPTIONAL
- -This function is called before
-
OPTIONAL
- -This function is called after
-
OPTIONAL
- -This function is called before
-
Note that it is not possible to add SCB's here right now, - that feature might be added later, - but it would right now break backwards compatability.
-OPTIONAL
- -This function is called after
-
OPTIONAL
- -This function is called before
-
OPTIONAL
- -This function is called after
-
OPTIONAL
- -This function is called before
-
OPTIONAL
- -This function is called after
-
OPTIONAL
- -This function is called whenever a testcase fails.
- It is called after the post function has been called for
- the testcase which failed. i.e.
- if init_per_suite fails this function is called after
-
The data which comes with the Reason follows the same format as the
-
OPTIONAL
- -This function is called whenever a testcase is skipped.
- It is called after the post function has been called for the
- testcase which was skipped.
- i.e. if init_per_group is skipped this function is called after
-
The data which comes with the Reason follows the same format as
-
OPTIONAL
- -This function is called at the end of an SCB's
-
OPTIONAL
- -The
This function should NOT have any side effects as it might - be called multiple times by Common Test.
- -If not implemented the SCB will act as if this function returned a
- call to
Test terms:
@@ -484,9 +484,9 @@ LogDir = string() EventHandlers = atom() | [atom()] InitArgs = [term()] - SCBModules = [SCBModule | {SCBModule, SCBInitArgs}] - SCBModule = atom() - SCBInitArgs = term() + CTHModules = [CTHModule | {CTHModule, CTHInitArgs}] + CTHModule = atom() + CTHInitArgs = term() DirRef = DirAlias | Dir Suites = atom() | [atom()] | all Suite = atom() diff --git a/lib/common_test/doc/src/suite_callbacks_chapter.xml b/lib/common_test/doc/src/suite_callbacks_chapter.xml deleted file mode 100644 index 89f78898d4..0000000000 --- a/lib/common_test/doc/src/suite_callbacks_chapter.xml +++ /dev/null @@ -1,394 +0,0 @@ - - - -- - - - - -- cgit v1.2.3- - -- -2011 2011 -Ericsson AB. 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. - - - -Suite Callbacks -Lukas Larsson -- - - suite_callbacks_chapter.xml -- - - -General -- This feature is in alpha release right now. This means that the - interface may change in the future and that there may be bugs. We - encourage you to use this feature, but be prepared - that there might be bugs and that the interface might change - inbetween releases.
- The Suite Callback (henceforth called SCB) framework allows - extensions of the default behaviour of Common Test by means of callbacks - before and after all test suite calls. SCBs allow advanced Common Test - users to abstract out behaviour which is common to multiple test suites - without littering all test suites with library calls. Some example - usages are: logging, starting and monitoring external systems, - building C files needed by the tests and much more!
- -In brief, Suite Callbacks allows you to:
- --
- -- Manipulate the runtime config before each suite - configuration calls
-- Manipulate the return of all suite configuration calls and in - extension the result of the test themselves.
-The following sections describe how to use SCBs, when they are run - and how to manipulate your test results in an SCB
- -- - When executing within an SCB all timetraps are shutoff. So - if your SCB never returns, the entire test run will be stalled!
-- - - -Installing an SCB -There are multiple ways to install an SCB in your test run. You can do it - for all tests in a run, for specific test suites and for specific groups - within a test suite. If you want an SCB to be present in all test suites - within your testrun there are three different ways to accomplish that.
- --
- -- Add
--suite_callbacks as an argument to -ct_run . - To add multiple SCBs using this method append them to each other - using the keywordand , i.e. -ct_run -suite_callbacks scb1 [{debug,true}] and scb2 ... .- Add the
-suite_callbacks tag to your -- Test Specification - Add the
-suite_callbacks tag to your call to -ct:run_test/1 You can also add SCBs within a test suite. This is done by returning -
- -{suite_callbacks,[SCB]} in the config list from -suite/0 , -- init_per_suite/1 or -- init_per_group/2 .SCB in this case can be either - only the module name of the SCB or a tuple with the module name and the - initial arguments to the SCB. Eg: -{suite_callbacks,[my_scb_module]} or -{suite_callbacks,[{my_scb_module,[{debug,true}]}]} - - -Overriding SCBs -By default each installation of an SCB will cause a new instance of it - to be activated. This can cause problems if you want to be able to - override SCBs in testspecifications while still having them in the - suite info function. The -
-id/1 - callback exists to address this problem. By returning the same -id in both places, Common Test knows that this SCB - has already been installed and will not try to install it again.- - - -SCB Scope -Once the SCB is installed into a certain test run it will be there until - it's scope is expired. The scope of an SCB depends on when it is - installed. - The
-init/2 is - called at the beginning of the scope and the -terminate/1 - function is called when the scope ends.-
- --
-SCB Installed in | -SCB scope begins before | -SCB scope ends after | --
-- | ct_run the first test suite is to be run. | -the last test suite has been run. | --
-- | ct:run_test the first test suite is to be run. | -the last test suite has been run. | --
-- | - Test Specification the first test suite is to be run. | -the last test suite has been run. | --
-- | suite/0 - - | - pre_init_per_suite/3 is called.- | - post_end_per_suite/4 has been called for that test suite.-
-- | - init_per_suite/1 - | - post_init_per_suite/4 is called.- | - post_end_per_suite/4 has been called for that test suite.-
-- | - init_per_group/2 - | - post_init_per_group/4 is called.- | - post_end_per_group/4 has been called for that group.Scope of an SCB -- - -CTH Processes and Tables -CTHs are run with the same process scoping as normal test suites - i.e. a different process will execute the init_per_suite hooks then the - init_per_group or per_testcase hooks. So if you want to spawn a - process in the CTH you cannot link with the CTH process as it will exit - after the post hook ends. Also if you for some reason need an ETS - table with your CTH, you will have to spawn a process which handles - it.
-- - - -Manipulating tests -It is through SCB's possible to manipulate the results of tests and - configuration functions. The main purpose of doing this with SCBs is to - allow common patterns to be abstracted out from test test suites and applied to - multiple test suites without duplicating any code. All of the callback - functions for an SCB follow a common interface, this interface is - described below.
- -- - - -Pre test manipulation -- It is possible in an SCB to hook in behaviour before -
-init_per_suite , -init_per_group , -init_per_testcase , -end_per_group and -end_per_suite . - This is done in the SCB functions called pre_<name of function>. - All of these function take the same three arguments:Name , -Config andSCBState . The return value of the SCB function - is always a combination of an result for the suite/group/test and an - updatedSCBState . If you want the test suite to continue on - executing you should return the config list which you want the test to - use as the result. If you for some reason want to skip/fail the test, - return a tuple withskip orfail and a reason as the - result. Example: -pre_init_per_suite(SuiteName, Config, SCBState) -> - case db:connect() of - {error,_Reason} -> - {{fail, "Could not connect to DB"}, SCBState}; - {ok, Handle} -> - {[{db_handle, Handle} | Config], SCBState#state{ handle = Handle }} - end.
- -- - - -Post test manipulation -It is also possible in an SCB to hook in behaviour after -
- -init_per_suite , -init_per_group , -end_per_testcase , -end_per_group and -end_per_suite . - This is done in the SCB functions called post_<name of function>. - All of these function take the same four arguments:Name , -Config ,Return andSCBState .Config in this - case is the sameConfig as the testcase is called with. -Return is the value returned by the testcase. If the testcase - failed by crashing,Return will be -{'EXIT',{{Error,Reason},Stacktrace}} .The return value of the SCB function is always a combination of an - result for the suite/group/test and an updated
- -SCBState . If - you want the callback to not affect the outcome of the test you should - return theReturn data as it is given to the SCB. You can also - modify the result of the test. By returning theConfig list - with thetc_status element removed you can recover from a test - failure. As in all the pre hooks, it is also possible to fail/skip - the test case in the post hook. Example:post_end_per_testcase(_TC, Config, {'EXIT',{_,_}}, SCBState) -> - case db:check_consistency() of - true -> - %% DB is good, pass the test. - {proplists:delete(tc_status, Config), SCBState}; - false -> - %% DB is not good, mark as skipped instead of failing - {{skip, "DB is inconsisten!"}, SCBState} - end; -post_end_per_testcase(_TC, Config, Return, SCBState) -> - %% Do nothing if tc does not crash. - {Return, SCBState}.
- -Recovering from a testcase failure using SCBs should only be done as - a last resort. If used wrongly it could become very difficult to - determine which tests pass or fail in a test run - -- - - -Skip and Fail -- After any post hook has been executed for all installed SCBs, -
-on_tc_fail - oron_tc_skip - might be called if the testcase failed or was skipped - respectively. You cannot affect the outcome of the tests any further at - this point. -- - - -Example SCB -The SCB below will log information about a test run into a format - parseable by
-file:consult/1 . -%%% @doc Common Test Example Suite Callback module. --module(example_scb). - -%% Suite Callbacks --export([id/1]). --export([init/2]). - --export([pre_init_per_suite/3]). --export([post_init_per_suite/4]). --export([pre_end_per_suite/3]). --export([post_end_per_suite/4]). - --export([pre_init_per_group/3]). --export([post_init_per_group/4]). --export([pre_end_per_group/3]). --export([post_end_per_group/4]). - --export([pre_init_per_testcase/3]). --export([post_end_per_testcase/4]). - --export([on_tc_fail/3]). --export([on_tc_skip/3]). - --export([terminate/1]). - --record(state, { file_handle, total, suite_total, ts, tcs, data }). - -%% @doc Return a unique id for this SCB. -id(Opts) -> - proplists:get_value(filename, Opts, "/tmp/file.log"). - -%% @doc Always called before any other callback function. Use this to initiate -%% any common state. -init(Id, Opts) -> - {ok,D} = file:open(Id,[write]), - #state{ file_handle = D, total = 0, data = [] }. - -%% @doc Called before init_per_suite is called. -pre_init_per_suite(Suite,Config,State) -> - {Config, State#state{ suite_total = 0, tcs = [] }}. - -%% @doc Called after init_per_suite. -post_init_per_suite(Suite,Config,Return,State) -> - {Return, State}. - -%% @doc Called before end_per_suite. -pre_end_per_suite(Suite,Config,State) -> - {Config, State}. - -%% @doc Called after end_per_suite. -post_end_per_suite(Suite,Config,Return,State) -> - Data = {suites, Suite, State#state.suite_total, lists:reverse(State#state.tcs)}, - {Return, State#state{ data = [Data | State#state.data] , - total = State#state.total + State#state.suite_total } }. - -%% @doc Called before each init_per_group. -pre_init_per_group(Group,Config,State) -> - {Config, State}. - -%% @doc Called after each init_per_group. -post_init_per_group(Group,Config,Return,State) -> - {Return, State}. - -%% @doc Called after each end_per_group. -pre_end_per_group(Group,Config,State) -> - {Config, State}. - -%% @doc Called after each end_per_group. -post_end_per_group(Group,Config,Return,State) -> - {Return, State}. - -%% @doc Called before each test case. -pre_init_per_testcase(TC,Config,State) -> - {Config, State#state{ ts = now(), total = State#state.suite_total + 1 } }. - -%% @doc Called after each test case. -post_end_per_testcase(TC,Config,Return,State) -> - TCInfo = {testcase, TC, Return, timer:now_diff(now(), State#state.ts)}, - {Return, State#state{ ts = undefined, tcs = [TCInfo | State#state.tcs] } }. - -%% @doc Called after post_init_per_suite, post_end_per_suite, post_init_per_group, -%% post_end_per_group and post_end_per_testcase if the suite, group or test case failed. -on_tc_fail(TC, Reason, State) -> - State. - -%% @doc Called when a test case is skipped by either user action -%% or due to an init function failing. -on_tc_skip(TC, Reason, State) -> - State. - -%% @doc Called when the scope of the SCB is done -terminate(State) -> - io:format(State#state.file_handle, "~p.~n", - [{test_run, State#state.total, State#state.data}]), - file:close(State#state.file_handle), - ok.
-