From 2ef840647acadb489d54332f6a218dcf2e629ff9 Mon Sep 17 00:00:00 2001
From: tmanevik To avoid hard coding data values related to the test and/or SUT (System
- Under Test) in the test suites, the data may instead be specified by means
- of configuration files or strings that Common Test reads before
+ To avoid hard-coding data values related to the test and/or System
+ Under Test (SUT) in the test suites, the data can instead be specified through
+ configuration files or strings that
A configuration file can contain any number of elements of the type:
- {CfgVarName,Value}.+ {CfgVarName,Value}.
where
- CfgVarName = atom() - Value = term() | [{CfgVarName,Value}]+ CfgVarName = atom() + Value = term() | [{CfgVarName,Value}]
In a test suite, one must require that a configuration
- variable (
A
A
To read the value of a config variable, use the function
-
Example:
+To read the value of a configuration variable, use function
+
Example:
- suite() -> - [{require, domain, 'CONN_SPEC_DNS_SUFFIX'}]. + suite() -> + [{require, domain, 'CONN_SPEC_DNS_SUFFIX'}]. - ... - - testcase(Config) -> - Domain = ct:get_config(domain), - ...+ ... + + testcase(Config) -> + Domain = ct:get_config(domain), + ...
If a configuration variable is defined in multiple files and you
- want to access all possible values, you may use the
It is possible to encrypt configuration files containing sensitive data - if these files must be stored in open and shared directories.
-Call
Configuration files containing sensitive data can be encrypted + if they must be stored in open and shared directories.
+To have
Common Test also provides decryption functions,
-
Please see the
There are two different methods for opening a connection
- by means of the support functions in e.g.
Two different methods for opening a connection using the support functions
+ in, for example,
When a target name is used for referencing the configuration data
- (that specifies the connection to be opened), the same name may be used
+ (that specifies the connection to be opened), the same name can be used
as connection identity in all subsequent calls related to the connection
- (also for closing it). It's only possible to have one open connection
- per target name. If attempting to open a new connection using a name
- already associated with an open connection, Common Test will
- return the already existing handle so that the previously opened connection
- will be used. This is a practical feature since it makes it possible to
+ (also for closing it). Only one open connection per target name
+ is possible. If you attempt to open a new connection using a name
+ already associated with an open connection,
When a configuration variable name is used as reference to the data specifying the connection, the handle returned as a result of opening the connection must be used in all subsequent calls (also for closing the connection). Repeated calls to the open function with the same - variable name as reference will result in multiple connections - being opened. This can be useful e.g. if a test case needs to open + variable name as reference results in multiple connections being opened. + This can be useful, for example, if a test case needs to open multiple connections to the same server on the target node (using the same configuration data for each connection).
It is possible for the user to specify configuration data on a +
The user can specify configuration data on a
different format than key-value tuples in a text file, as described
- so far. The data can e.g. be read from arbitrary files, fetched from
- the web over http, or requested from a user specific process.
- To support this, Common Test provides a callback module plugin
+ so far. The data can, for example, be read from any files, fetched from
+ the web over HTTP, or requested from a user-specific process.
+ To support this,
The Common Test application includes default callback modules - for handling configuration data specified in standard config files - (see above) and in xml files:
-This is an example of an XML configuration file:
-+-Using XML Configuration Files +An example of an XML configuration file follows:
+++ ]]>"targethost" "tester" "letmein" "/test/loadmodules" -]]>
This configuration file, once read, will produce the same configuration +
Once read, this file produces the same configuration variables as the following text file:
-{ftp_host, [{ftp,"targethost"}, - {username,"tester"}, - {password,"letmein"}]}. + {ftp_host, [{ftp,"targethost"}, + {username,"tester"}, + {password,"letmein"}]}. -{lm_directory, "/test/loadmodules"}.+ {lm_directory, "/test/loadmodules"}.
The user specific handler can be written to handle special +
The user-specific handler can be written to handle special configuration file formats. The parameter can be either file - name(s) or configuration string(s) (the empty list is valid).
+ names or configuration strings (the empty list is valid).The callback module implementing the handler is responsible for - checking correctness of configuration strings.
+ checking the correctness of configuration strings. -To perform validation of the configuration strings, the callback module - should have the following function exported:
+To validate the configuration strings, the callback module
+ is to have function
The input argument will be passed from Common Test, as defined in the test
- specification or given as an option to
The input argument is passed from
The return value should be any of the following values indicating if given +
The return value is to be any of the following values, indicating if the specified configuration parameter is valid:
-To perform reading of configuration data - initially before the tests - start, or as a result of data being reloaded during test execution - - the following function should be exported from the callback module:
- -The function
The input argument is the same as for the
The return value should be either:
+The return value is to be either of the following:
-- [{ftp_host, [{ftp, "targethost"}, {username, "tester"}, {password, "letmein"}]}, - {lm_directory, "/test/loadmodules"}]+ [{ftp_host, [{ftp, "targethost"}, {username, "tester"}, {password, "letmein"}]}, + {lm_directory, "/test/loadmodules"}]
A config file for using the FTP client to access files on a remote - host could look like this:
+A configuration file for using the FTP client to access files on a remote + host can look as follows:
- {ftp_host, [{ftp,"targethost"}, - {username,"tester"}, - {password,"letmein"}]}. + {ftp_host, [{ftp,"targethost"}, + {username,"tester"}, + {password,"letmein"}]}. - {lm_directory, "/test/loadmodules"}.+ {lm_directory, "/test/loadmodules"}. -
The XML version shown in the chapter above can also be used, but it should be +
The XML version shown earlier can also be used, but it is to be
explicitly specified that the
Example of how to assert that the configuration data is available and - use it for an FTP session:
+The following is an example of how to assert that the configuration data is available + and can be used for an FTP session:
- init_per_testcase(ftptest, Config) -> - {ok,_} = ct_ftp:open(ftp), - Config. - - end_per_testcase(ftptest, _Config) -> - ct_ftp:close(ftp). - - ftptest() -> - [{require,ftp,ftp_host}, - {require,lm_directory}]. - - ftptest(Config) -> - Remote = filename:join(ct:get_config(lm_directory), "loadmodX"), - Local = filename:join(?config(priv_dir,Config), "loadmodule"), - ok = ct_ftp:recv(ftp, Remote, Local), - ...+ init_per_testcase(ftptest, Config) -> + {ok,_} = ct_ftp:open(ftp), + Config. + + end_per_testcase(ftptest, _Config) -> + ct_ftp:close(ftp). + + ftptest() -> + [{require,ftp,ftp_host}, + {require,lm_directory}]. + + ftptest(Config) -> + Remote = filename:join(ct:get_config(lm_directory), "loadmodX"), + Local = filename:join(?config(priv_dir,Config), "loadmodule"), + ok = ct_ftp:recv(ftp, Remote, Local), + ... -
An example of how the above functions could be rewritten - if necessary to open multiple connections to the FTP server:
+The following is an example of how the functions in the previous example + can be rewritten if it is necessary to open multiple connections to the + FTP server:
- init_per_testcase(ftptest, Config) -> - {ok,Handle1} = ct_ftp:open(ftp_host), - {ok,Handle2} = ct_ftp:open(ftp_host), - [{ftp_handles,[Handle1,Handle2]} | Config]. - - end_per_testcase(ftptest, Config) -> - lists:foreach(fun(Handle) -> ct_ftp:close(Handle) end, - ?config(ftp_handles,Config)). - - ftptest() -> - [{require,ftp_host}, - {require,lm_directory}]. - - ftptest(Config) -> - Remote = filename:join(ct:get_config(lm_directory), "loadmodX"), - Local = filename:join(?config(priv_dir,Config), "loadmodule"), - [Handle | MoreHandles] = ?config(ftp_handles,Config), - ok = ct_ftp:recv(Handle, Remote, Local), - ...+ init_per_testcase(ftptest, Config) -> + {ok,Handle1} = ct_ftp:open(ftp_host), + {ok,Handle2} = ct_ftp:open(ftp_host), + [{ftp_handles,[Handle1,Handle2]} | Config]. + + end_per_testcase(ftptest, Config) -> + lists:foreach(fun(Handle) -> ct_ftp:close(Handle) end, + ?config(ftp_handles,Config)). + + ftptest() -> + [{require,ftp_host}, + {require,lm_directory}]. + + ftptest(Config) -> + Remote = filename:join(ct:get_config(lm_directory), "loadmodX"), + Local = filename:join(?config(priv_dir,Config), "loadmodule"), + [Handle | MoreHandles] = ?config(ftp_handles,Config), + ok = ct_ftp:recv(Handle, Remote, Local), + ...
A simple configuration handling driver which will ask an external server for - configuration data can be implemented this way:
+A simple configuration handling driver, asking an external server for + configuration data, can be implemented as follows:
--module(config_driver). --export([read_config/1, check_parameter/1]). - -read_config(ServerName)-> - ServerModule = list_to_atom(ServerName), - ServerModule:start(), - ServerModule:get_config(). - -check_parameter(ServerName)-> - ServerModule = list_to_atom(ServerName), - case code:is_loaded(ServerModule) of - {file, _}-> - {ok, {config, ServerName}}; - false-> - case code:load_file(ServerModule) of - {module, ServerModule}-> - {ok, {config, ServerName}}; - {error, nofile}-> - {error, {wrong_config, "File not found: " ++ ServerName ++ ".beam"}} - end - end.- -
The configuration string for this driver may be "config_server", if the - config_server.erl module below is compiled and exists in the code path + -module(config_driver). + -export([read_config/1, check_parameter/1]). + + read_config(ServerName)-> + ServerModule = list_to_atom(ServerName), + ServerModule:start(), + ServerModule:get_config(). + + check_parameter(ServerName)-> + ServerModule = list_to_atom(ServerName), + case code:is_loaded(ServerModule) of + {file, _}-> + {ok, {config, ServerName}}; + false-> + case code:load_file(ServerModule) of + {module, ServerModule}-> + {ok, {config, ServerName}}; + {error, nofile}-> + {error, {wrong_config, "File not found: " ++ ServerName ++ ".beam"}} + end + end. + +
The configuration string for this driver can be
--module(config_server). --export([start/0, stop/0, init/1, get_config/0, loop/0]). - --define(REGISTERED_NAME, ct_test_config_server). - -start()-> - case whereis(?REGISTERED_NAME) of - undefined-> - spawn(?MODULE, init, [?REGISTERED_NAME]), - wait(); - _Pid-> - ok - end, - ?REGISTERED_NAME. - -init(Name)-> - register(Name, self()), - loop(). - -get_config()-> - call(self(), get_config). - -stop()-> - call(self(), stop). - -call(Client, Request)-> - case whereis(?REGISTERED_NAME) of - undefined-> - {error, {not_started, Request}}; - Pid-> - Pid ! {Client, Request}, - receive - Reply-> - {ok, Reply} - after 4000-> - {error, {timeout, Request}} - end - end. - -loop()-> - receive - {Pid, stop}-> - Pid ! ok; - {Pid, get_config}-> - {D,T} = erlang:localtime(), - Pid ! - [{localtime, [{date, D}, {time, T}]}, - {node, erlang:node()}, - {now, erlang:now()}, - {config_server_pid, self()}, - {config_server_vsn, ?vsn}], - ?MODULE:loop() - end. - -wait()-> - case whereis(?REGISTERED_NAME) of - undefined-> - wait(); - _Pid-> - ok - end.- -
In this example, the handler also provides the ability to dynamically reload
- configuration variables. If
Here, the handler also provides for dynamically reloading of
+ configuration variables. If
+