diff options
Diffstat (limited to 'lib/common_test/doc/src/config_file_chapter.xml')
-rw-r--r-- | lib/common_test/doc/src/config_file_chapter.xml | 558 |
1 files changed, 277 insertions, 281 deletions
diff --git a/lib/common_test/doc/src/config_file_chapter.xml b/lib/common_test/doc/src/config_file_chapter.xml index c7fd6e0b28..62ebfccb98 100644 --- a/lib/common_test/doc/src/config_file_chapter.xml +++ b/lib/common_test/doc/src/config_file_chapter.xml @@ -35,18 +35,18 @@ <section> <title>General</title> - <p>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 + <p>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 <c>Common Test</c> reads before the start of a test run. External configuration data makes it possible to - change test properties without having to modify the actual test suites - using the data. Examples of configuration data:</p> + change test properties without modifying the test suites + using the data. Examples of configuration data follows:</p> - <list> + <list type="bulleted"> <item>Addresses to the test plant or other instruments</item> <item>User login information</item> <item>Names of files needed by the test</item> - <item>Names of programs that should be executed during the test</item> + <item>Names of programs to be executed during the test</item> <item>Any other variable needed by the test</item> </list> @@ -57,154 +57,150 @@ <p>A configuration file can contain any number of elements of the type:</p> <pre> - {CfgVarName,Value}.</pre> + {CfgVarName,Value}.</pre> <p>where</p> <pre> - CfgVarName = atom() - Value = term() | [{CfgVarName,Value}]</pre> + CfgVarName = atom() + Value = term() | [{CfgVarName,Value}]</pre> </section> <section> - <title>Requiring and reading configuration data</title> + <title>Requiring and Reading Configuration Data</title> <marker id="require_config_data"></marker> <p>In a test suite, one must <em>require</em> that a configuration - variable (<c>CfgVarName</c> in the definition above) exists before - attempting to read the associated value in a test case or config function.</p> - - <p><c>require</c> is an assert statement that can be part of the <seealso - marker="write_test_chapter#suite">test suite info function</seealso> or - <seealso marker="write_test_chapter#info_function">test case info - function</seealso>. If the required variable is not available, the - test is skipped (unless a default value has been specified, see the - <seealso marker="write_test_chapter#info_function">test case info - function</seealso> chapter for details). There is also a function - <seealso marker="ct#require-1"><c>ct:require/1/2</c></seealso> which can be called from a test case - in order to check if a specific variable is available. The return + variable (<c>CfgVarName</c> in the previous definition) exists before + attempting to read the associated value in a test case or configuration function.</p> + + <p><c>require</c> is an assert statement, which can be part of the <seealso + marker="write_test_chapter#suite">Test Suite Information Function</seealso> or + <seealso marker="write_test_chapter#info_function">Test Case Information + Function</seealso>. If the required variable is unavailable, the + test is skipped (unless a default value has been specified, see section + <seealso marker="write_test_chapter#info_function">Test Case Information + Function</seealso> for details). Also, function + <seealso marker="ct#require-1"><c>ct:require/1/2</c></seealso> can be called + from a test case to check if a specific variable is available. The return value from this function must be checked explicitly and appropriate - action be taken depending on the result (e.g. to skip the test case - if the variable in question doesn't exist).</p> + action be taken depending on the result (for example, to skip the test case + if the variable in question does not exist).</p> - <p>A <c>require</c> statement in the test suite info- or test case - info-list should look like this: + <p>A <c>require</c> statement in the test suite information case or test case + information-list is to look like <c>{require,CfgVarName}</c> or <c>{require,AliasName,CfgVarName}</c>. The arguments <c>AliasName</c> and <c>CfgVarName</c> are the same as the - arguments to <seealso marker="ct#require-1"><c>ct:require/1/2</c></seealso> which are described in the - reference manual for <seealso marker="ct">ct</seealso>. + arguments to <seealso marker="ct#require-1"><c>ct:require/1,2</c></seealso>. <c>AliasName</c> becomes an alias for the configuration variable, and can be used as reference to the configuration data value. - The configuration variable may be associated with an - arbitrary number of alias names, but each name must be unique within - the same test suite. There are two main uses for alias names:</p> - <list> - <item>They may be introduced to identify connections (see below).</item> - <item>They may used to help adapt configuration data to a test suite + The configuration variable can be associated with any + number of alias names, but each name must be unique within + the same test suite. The two main uses for alias names follows:</p> + <list type="bulleted"> + <item>To identify connections (described later).</item> + <item>To help adapt configuration data to a test suite (or test case) and improve readability.</item> </list> - <p>To read the value of a config variable, use the function - <seealso marker="ct#get_config-1"><c>get_config/1/2/3</c></seealso> - which is also described in the reference - manual for <seealso marker="ct">ct</seealso>.</p> - <p>Example:</p> + <p>To read the value of a configuration variable, use function + <seealso marker="ct#get_config-1"><c>get_config/1,2,3</c></seealso>. + </p> + <p><em>Example:</em></p> <pre> - suite() -> - [{require, domain, 'CONN_SPEC_DNS_SUFFIX'}]. + suite() -> + [{require, domain, 'CONN_SPEC_DNS_SUFFIX'}]. - ... - - testcase(Config) -> - Domain = ct:get_config(domain), - ...</pre> + ... + + testcase(Config) -> + Domain = ct:get_config(domain), + ...</pre> </section> <section> - <title>Using configuration variables defined in multiple files</title> + <title>Using Configuration Variables Defined in Multiple Files</title> <p>If a configuration variable is defined in multiple files and you - want to access all possible values, you may use the <seealso marker="ct#get_config-3"><c>ct:get_config/3</c></seealso> - function and specify <c>all</c> in the options list. The values will then - be returned in a list and the order of the elements corresponds to the order - that the config files were specified at startup. Please see - the <seealso marker="ct">ct</seealso> reference manual for details.</p> + want to access all possible values, use function + <seealso marker="ct#get_config-3"><c>ct:get_config/3</c></seealso> + and specify <c>all</c> in the options list. The values are then + returned in a list and the order of the elements corresponds to the order + that the configuration files were specified at startup.</p> </section> <section> - <title>Encrypted configuration files</title> + <title>Encrypted Configuration Files</title> <marker id="encrypted_config_files"></marker> - <p>It is possible to encrypt configuration files containing sensitive data - if these files must be stored in open and shared directories.</p> - <p>Call <seealso marker="ct#encrypt_config_file-2"><c>ct:encrypt_config_file/2/3</c></seealso> to have Common Test encrypt a - specified file using the DES3 function in the OTP <c>crypto</c> application. - The encrypted file can then be used as a regular configuration file, - in combination with other encrypted files or normal text files. The key - for decrypting the configuration file must be provided when running the test, - however. This can be done by means of the <c>decrypt_key</c> or - <c>decrypt_file</c> flag/option, or a key file in a predefined location.</p> + <p>Configuration files containing sensitive data can be encrypted + if they must be stored in open and shared directories.</p> + <p>To have <c>Common Test</c> encrypt a + specified file using function <c>DES3</c> in application <c>Crypto</c>, + call <seealso marker="ct#encrypt_config_file-2"><c>ct:encrypt_config_file/2,3</c></seealso> + The encrypted file can then be used as a regular configuration file + in combination with other encrypted files or normal text files. However, the + key for decrypting the configuration file must be provided when running the test. + This can be done with flag/option <c>decrypt_key</c> or + <c>decrypt_file</c>, or a key file in a predefined location.</p> - <p>Common Test also provides decryption functions, - <seealso marker="ct#decrypt_config_file-2"><c>ct:decrypt_config_file/2/3</c></seealso>, for recreating the original text - files.</p> - - <p>Please see the <seealso marker="ct">ct</seealso> reference manual for - more information.</p> + <p><c>Common Test</c> also provides decryption functions, + <seealso marker="ct#decrypt_config_file-2"><c>ct:decrypt_config_file/2,3</c></seealso>, + for recreating the original text files.</p> </section> <section> - <title>Opening connections by using configuration data</title> - <p>There are two different methods for opening a connection - by means of the support functions in e.g. <seealso marker="ct_ssh"><c>ct_ssh</c></seealso>, <seealso marker="ct_ftp"><c>ct_ftp</c></seealso>, - and <seealso marker="ct_telnet"><c>ct_telnet</c></seealso>:</p> - <list> + <title>Opening Connections Using Configuration Data</title> + <p>Two different methods for opening a connection using the support functions + in, for example, <seealso marker="ct_ssh"><c>ct_ssh</c></seealso>, + <seealso marker="ct_ftp"><c>ct_ftp</c></seealso>, and + <seealso marker="ct_telnet"><c>ct_telnet</c></seealso> follows:</p> + <list type="bulleted"> <item>Using a configuration target name (an alias) as reference.</item> <item>Using the configuration variable as reference.</item> </list> <p>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, <c>Common Test</c> + returns the already existing handle so the previously opened connection + is used. This feature makes it possible to call the function for opening a particular connection whenever - useful. An action like this will not necessarily open any new - connections unless it's required (which could be the case if e.g. the - previous connection has been closed unexpectedly by the server). - Another benefit of using named connections is that it's not - necessary to pass handle references around in the suite for these - connections. + useful. An action like this does not necessarily open any new + connections unless it is required (which could be the case if, for example, + the previous connection has been closed unexpectedly by the server). + Using named connections also removes the need to pass handle references + around in the suite for these connections. </p> <p>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). </p> </section> <section> - <title>User specific configuration data formats</title> + <title>User-Specific Configuration Data Formats</title> - <p>It is possible for the user to specify configuration data on a + <p>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, <c>Common Test</c> provides a callback module plugin mechanism to handle configuration data.</p> <section> - <title>Default callback modules for handling configuration data</title> - <p>The Common Test application includes default callback modules - for handling configuration data specified in standard config files - (see above) and in xml files:</p> - <list> + <title>Default Callback Modules for Handling Configuration Data</title> + <p><c>Common Test</c> includes default callback modules + for handling configuration data specified in standard configuration files + (described earlier) and in XML files as follows:</p> + <list type="bulleted"> <item> <c>ct_config_plain</c> - for reading configuration files with - key-value tuples (standard format). This handler will be used to + key-value tuples (standard format). This handler is used to parse configuration files if no user callback is specified. </item> <item> @@ -215,59 +211,59 @@ </section> <section> - <title>Using XML configuration files</title> - <p>This is an example of an XML configuration file:</p> - <pre><![CDATA[ -<config> + <title>Using XML Configuration Files</title> + <p>An example of an XML configuration file follows:</p> + <pre> + <![CDATA[ + <config> <ftp_host> <ftp>"targethost"</ftp> <username>"tester"</username> <password>"letmein"</password> </ftp_host> <lm_directory>"/test/loadmodules"</lm_directory> -</config>]]></pre> + </config>]]></pre> - <p>This configuration file, once read, will produce the same configuration + <p>Once read, this file produces the same configuration variables as the following text file:</p> <pre> -{ftp_host, [{ftp,"targethost"}, - {username,"tester"}, - {password,"letmein"}]}. + {ftp_host, [{ftp,"targethost"}, + {username,"tester"}, + {password,"letmein"}]}. -{lm_directory, "/test/loadmodules"}.</pre> + {lm_directory, "/test/loadmodules"}.</pre> </section> <section> - <title>How to implement a user specific handler</title> + <title>Implement a User-Specific Handler</title> - <p>The user specific handler can be written to handle special + <p>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).</p> + names or configuration strings (the empty list is valid).</p> <p>The callback module implementing the handler is responsible for - checking correctness of configuration strings.</p> + checking the correctness of configuration strings.</p> - <p>To perform validation of the configuration strings, the callback module - should have the following function exported:</p> + <p>To validate the configuration strings, the callback module + is to have function <c>Callback:check_parameter/1</c> exported.</p> - <p><c>Callback:check_parameter/1</c></p> - <p>The input argument will be passed from Common Test, as defined in the test - specification or given as an option to <c>ct_run</c> or <c>ct:run_test</c>.</p> + <p>The input argument is passed from <c>Common Test</c>, as defined in the test + specification, or specified as an option to <c>ct_run</c> or <c>ct:run_test</c>.</p> - <p>The return value should be any of the following values indicating if given + <p>The return value is to be any of the following values, indicating if the specified configuration parameter is valid:</p> - <list> + <list type="bulleted"> <item> - <c>{ok, {file, FileName}}</c> - parameter is a file name and - the file exists, + <c>{ok, {file, FileName}}</c> - the parameter is a file name and + the file exists. </item> <item> - <c>{ok, {config, ConfigString}}</c> - parameter is a config string - and it is correct, + <c>{ok, {config, ConfigString}}</c> - the parameter is a configuration string + and it is correct. </item> <item> - <c>{error, {nofile, FileName}}</c> - there is no file with the given - name in the current directory, + <c>{error, {nofile, FileName}}</c> - there is no file with the specified + name in the current directory. </item> <item> <c>{error, {wrong_config, ConfigString}}</c> - the configuration string @@ -275,196 +271,196 @@ </item> </list> - <p>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:</p> - - <p><c>Callback:read_config/1</c></p> + <p>The function <c>Callback:read_config/1</c> is to be exported from the + callback module to read configuration data, initially before the tests + start, or as a result of data being reloaded during test execution. + The input argument is the same as for function <c>check_parameter/1</c>.</p> - <p>The input argument is the same as for the <c>check_parameter/1</c> function.</p> - <p>The return value should be either:</p> + <p>The return value is to be either of the following:</p> - <list> + <list type="bulleted"> <item> - <c>{ok, Config}</c> - if the configuration variables are read successfully, + <c>{ok, Config}</c> - if the configuration variables are read successfully. </item> <item> <c>{error, {Error, ErrorDetails}}</c> - if the callback module fails to - proceed with the given configuration parameters. + proceed with the specified configuration parameters. </item> </list> <p><c>Config</c> is the proper Erlang key-value list, with possible - key-value sublists as values, like for the configuration file - example above:</p> + key-value sublists as values, like the earlier configuration file + example:</p> <pre> - [{ftp_host, [{ftp, "targethost"}, {username, "tester"}, {password, "letmein"}]}, - {lm_directory, "/test/loadmodules"}]</pre> + [{ftp_host, [{ftp, "targethost"}, {username, "tester"}, {password, "letmein"}]}, + {lm_directory, "/test/loadmodules"}]</pre> </section> </section> <section> - <title>Examples of configuration data handling</title> + <title>Examples of Configuration Data Handling</title> - <p>A config file for using the FTP client to access files on a remote - host could look like this:</p> + <p>A configuration file for using the FTP client to access files on a remote + host can look as follows:</p> <pre> - {ftp_host, [{ftp,"targethost"}, - {username,"tester"}, - {password,"letmein"}]}. + {ftp_host, [{ftp,"targethost"}, + {username,"tester"}, + {password,"letmein"}]}. - {lm_directory, "/test/loadmodules"}.</pre> + {lm_directory, "/test/loadmodules"}.</pre> - <p>The XML version shown in the chapter above can also be used, but it should be + <p>The XML version shown earlier can also be used, but it is to be explicitly specified that the <c>ct_config_xml</c> callback module is to be - used by Common Test.</p> + used by <c>Common Test</c>.</p> - <p>Example of how to assert that the configuration data is available and - use it for an FTP session:</p> + <p>The following is an example of how to assert that the configuration data is available + and can be used for an FTP session:</p> <pre> - 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), - ...</pre> + 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), + ...</pre> - <p>An example of how the above functions could be rewritten - if necessary to open multiple connections to the FTP server:</p> + <p>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:</p> <pre> - 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), - ...</pre> + 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), + ...</pre> </section> <section> - <title>Example of user specific configuration handler</title> - <p>A simple configuration handling driver which will ask an external server for - configuration data can be implemented this way:</p> + <title>Example of User-Specific Configuration Handler</title> + <p>A simple configuration handling driver, asking an external server for + configuration data, can be implemented as follows:</p> <pre> --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.</pre> - - <p>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.</pre> + + <p>The configuration string for this driver can be <c>config_server</c>, if the + <c>config_server.erl</c> module that follows is compiled and exists in the code path during test execution:</p> <pre> --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.</pre> - - <p>In this example, the handler also provides the ability to dynamically reload - configuration variables. If <c>ct:reload_config(localtime)</c> is called from + -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.</pre> + + <p>Here, the handler also provides for dynamically reloading of + configuration variables. If + <seealso marker="ct#reload_config-1"><c>ct:reload_config(localtime)</c></seealso> is called from the test case function, all variables loaded with <c>config_driver:read_config/1</c> - will be updated with their latest values, and the new value for variable - <c>localtime</c> will be returned.</p> + are updated with their latest values, and the new value for variable + <c>localtime</c> is returned.</p> </section> </chapter> |