From a3a33bd5fcca1d21eecbf5b4332716dd6e191056 Mon Sep 17 00:00:00 2001 From: Mickael Remond Date: Thu, 24 Mar 2016 10:02:13 +0100 Subject: [PATCH] Allow running test groups independently We need to be able to run only a few test groups, even if we do not have all database backends installed and configured locally. ejabberd test suite configures a specific host per backend. I changed ejabberd to allow ignoring some hosts from config file on start, by providing the exact list of hosts we want to start. This is done by setting an ejabberd app Erlang environment variable 'hosts' and passing the list of hosts we want to actually define. When doing so, the backend specific hosts defined in ejabberd test configuration file are simply ignored. As a result, we do not try to connect to unavailable backends. I linked that part to CT run test by defining the hosts list based on environment variable CT_BACKENDS. This variable is expected to be a comma separated list of available backends. When Erlang Common Tests are run with that environment variable set, only the host matching the name of the backend will be set, plus the default "localhost", common to many tests. This can be combined with rebar ct groups list. Example commands to run tests: CT_BACKENDS=riak,mnesia rebar ct suites=ejabberd CT_BACKENDS=mnesia rebar ct suites=ejabberd groups=mnesia --- src/ejabberd_config.erl | 71 +++++++++++++++++++++++++++++------------ test/ejabberd_SUITE.erl | 65 ++++++++++++++++++++++++++++--------- test/suite.erl | 14 +++++++- 3 files changed, 114 insertions(+), 36 deletions(-) diff --git a/src/ejabberd_config.erl b/src/ejabberd_config.erl index eb06b98f6..6f7368b7c 100644 --- a/src/ejabberd_config.erl +++ b/src/ejabberd_config.erl @@ -38,6 +38,8 @@ convert_to_yaml/1, convert_to_yaml/2, env_binary_to_list/2, opt_type/1, may_hide_data/1]). +-export([start/2]). + -include("ejabberd.hrl"). -include("logger.hrl"). -include("ejabberd_config.hrl"). @@ -52,8 +54,48 @@ %% @type macro_value() = term(). - start() -> + mnesia_init(), + Config = get_ejabberd_config_path(), + State0 = read_file(Config), + State1 = hosts_to_start(State0), + State2 = validate_opts(State1), + %% This start time is used by mod_last: + UnixTime = p1_time_compat:system_time(seconds), + SharedKey = case erlang:get_cookie() of + nocookie -> + p1_sha:sha(randoms:get_string()); + Cookie -> + p1_sha:sha(jlib:atom_to_binary(Cookie)) + end, + State3 = set_option({node_start, global}, UnixTime, State2), + State4 = set_option({shared_key, global}, SharedKey, State3), + set_opts(State4). + +%% When starting ejabberd for testing, we sometimes want to start a +%% subset of hosts from the one define in the config file. +%% This function override the host list read from config file by the +%% one we provide. +%% Hosts to start are defined in an ejabberd application environment +%% variable 'hosts' to make it easy to ignore some host in config +%% file. +hosts_to_start(State) -> + case application:get_env(ejabberd, hosts) of + undefined -> + %% Start all hosts as defined in config file + State; + {ok, Hosts} -> + set_hosts_in_options(Hosts, State) + end. + +%% @private +%% At the moment, these functions are mainly used to setup unit tests. +-spec(start/2 :: (Hosts :: [binary()], Opts :: [acl:acl() | local_config()]) -> ok). +start(Hosts, Opts) -> + mnesia_init(), + set_opts(#state{hosts = Hosts, opts = Opts}). + +mnesia_init() -> case catch mnesia:table_info(local_config, storage_type) of disc_copies -> mnesia:delete_table(local_config); @@ -64,21 +106,7 @@ start() -> [{ram_copies, [node()]}, {local_content, true}, {attributes, record_info(fields, local_config)}]), - mnesia:add_table_copy(local_config, node(), ram_copies), - Config = get_ejabberd_config_path(), - State0 = read_file(Config), - State = validate_opts(State0), - %% This start time is used by mod_last: - UnixTime = p1_time_compat:system_time(seconds), - SharedKey = case erlang:get_cookie() of - nocookie -> - p1_sha:sha(randoms:get_string()); - Cookie -> - p1_sha:sha(jlib:atom_to_binary(Cookie)) - end, - State1 = set_option({node_start, global}, UnixTime, State), - State2 = set_option({shared_key, global}, SharedKey, State1), - set_opts(State2). + mnesia:add_table_copy(local_config, node(), ram_copies). %% @doc Get the filename of the ejabberd configuration file. %% The filename can be specified with: erl -config "/path/to/ejabberd.yml". @@ -277,7 +305,7 @@ search_hosts(Term, State) -> {host, Host} -> if State#state.hosts == [] -> - add_hosts_to_option([Host], State); + set_hosts_in_options([Host], State); true -> ?ERROR_MSG("Can't load config file: " "too many hosts definitions", []), @@ -286,7 +314,7 @@ search_hosts(Term, State) -> {hosts, Hosts} -> if State#state.hosts == [] -> - add_hosts_to_option(Hosts, State); + set_hosts_in_options(Hosts, State); true -> ?ERROR_MSG("Can't load config file: " "too many hosts definitions", []), @@ -296,9 +324,12 @@ search_hosts(Term, State) -> State end. -add_hosts_to_option(Hosts, State) -> +set_hosts_in_options(Hosts, State) -> PrepHosts = normalize_hosts(Hosts), - set_option({hosts, global}, PrepHosts, State#state{hosts = PrepHosts}). + NewOpts = lists:filter(fun({local_config,{hosts,global},_}) -> false; + (_) -> true + end, State#state.opts), + set_option({hosts, global}, PrepHosts, State#state{hosts = PrepHosts, opts = NewOpts}). normalize_hosts(Hosts) -> normalize_hosts(Hosts,[]). diff --git a/test/ejabberd_SUITE.erl b/test/ejabberd_SUITE.erl index b239ddf0b..b9ba9956a 100644 --- a/test/ejabberd_SUITE.erl +++ b/test/ejabberd_SUITE.erl @@ -35,22 +35,57 @@ init_per_suite(Config) -> LDIFFile = filename:join([DataDir, "ejabberd.ldif"]), {ok, _} = file:copy(ExtAuthScript, filename:join([CWD, "extauth.py"])), {ok, _} = ldap_srv:start(LDIFFile), - ok = application:start(ejabberd), + start_ejabberd(NewConfig), NewConfig. -end_per_suite(_Config) -> - ok. +start_ejabberd(Config) -> + case proplists:get_value(backends, Config) of + all -> + ok = application:start(ejabberd, transient); + Backends when is_list(Backends) -> + Hosts = lists:map(fun(Backend) -> Backend ++ ".localhost" end, Backends), + application:load(ejabberd), + AllHosts = Hosts ++ ["localhost"], %% We always need localhost for the generic no_db tests + application:set_env(ejabberd, hosts, AllHosts), + ok = application:start(ejabberd, transient) + end. -init_per_group(no_db, Config) -> +end_per_suite(_Config) -> + application:stop(ejabberd). + +-define(BACKENDS, [mnesia,redis,mysql,pgsql,sqlite,ldap,extauth,riak]). + +init_per_group(Group, Config) -> + case lists:member(Group, ?BACKENDS) of + false -> + %% Not a backend related group, do default init: + do_init_per_group(Group, Config); + true -> + case proplists:get_value(backends, Config) of + all -> + %% All backends enabled + do_init_per_group(Group, Config); + Backends -> + %% Skipped backends that were not explicitely enabled + case lists:member(atom_to_list(Group), Backends) of + true -> + do_init_per_group(Group, Config); + false -> + {skip, {disabled_backend, Group}} + end + end + end. + +do_init_per_group(no_db, Config) -> re_register(Config), Config; -init_per_group(mnesia, Config) -> +do_init_per_group(mnesia, Config) -> mod_muc:shutdown_rooms(?MNESIA_VHOST), set_opt(server, ?MNESIA_VHOST, Config); -init_per_group(redis, Config) -> +do_init_per_group(redis, Config) -> mod_muc:shutdown_rooms(?REDIS_VHOST), set_opt(server, ?REDIS_VHOST, Config); -init_per_group(mysql, Config) -> +do_init_per_group(mysql, Config) -> case catch ejabberd_odbc:sql_query(?MYSQL_VHOST, [<<"select 1;">>]) of {selected, _, _} -> mod_muc:shutdown_rooms(?MYSQL_VHOST), @@ -59,7 +94,7 @@ init_per_group(mysql, Config) -> Err -> {skip, {mysql_not_available, Err}} end; -init_per_group(pgsql, Config) -> +do_init_per_group(pgsql, Config) -> case catch ejabberd_odbc:sql_query(?PGSQL_VHOST, [<<"select 1;">>]) of {selected, _, _} -> mod_muc:shutdown_rooms(?PGSQL_VHOST), @@ -68,7 +103,7 @@ init_per_group(pgsql, Config) -> Err -> {skip, {pgsql_not_available, Err}} end; -init_per_group(sqlite, Config) -> +do_init_per_group(sqlite, Config) -> case catch ejabberd_odbc:sql_query(?SQLITE_VHOST, [<<"select 1;">>]) of {selected, _, _} -> mod_muc:shutdown_rooms(?SQLITE_VHOST), @@ -76,11 +111,11 @@ init_per_group(sqlite, Config) -> Err -> {skip, {sqlite_not_available, Err}} end; -init_per_group(ldap, Config) -> +do_init_per_group(ldap, Config) -> set_opt(server, ?LDAP_VHOST, Config); -init_per_group(extauth, Config) -> +do_init_per_group(extauth, Config) -> set_opt(server, ?EXTAUTH_VHOST, Config); -init_per_group(riak, Config) -> +do_init_per_group(riak, Config) -> case ejabberd_riak:is_connected() of true -> mod_muc:shutdown_rooms(?RIAK_VHOST), @@ -89,7 +124,7 @@ init_per_group(riak, Config) -> Err -> {skip, {riak_not_available, Err}} end; -init_per_group(_GroupName, Config) -> +do_init_per_group(_GroupName, Config) -> Pid = start_event_relay(), set_opt(event_relay, Pid, Config). @@ -967,9 +1002,9 @@ mix_master(Config) -> retract = [ParticipantID]}]}]}), disconnect(Config). -mix_slave(Config) -> +mix_slave(Config) -> disconnect(Config). - + roster_subscribe_master(Config) -> send(Config, #presence{}), ?recv1(#presence{}), diff --git a/test/suite.erl b/test/suite.erl index bc094fa31..8000e1d29 100644 --- a/test/suite.erl +++ b/test/suite.erl @@ -65,9 +65,21 @@ init_config(Config) -> {resource, <<"resource">>}, {master_resource, <<"master_resource">>}, {slave_resource, <<"slave_resource">>}, - {password, <<"password">>} + {password, <<"password">>}, + {backends, get_config_backends()} |Config]. +%% Read environment variable CT_DB=riak,mysql to limit the backends to test. +%% You can thus limit the backend you want to test with: +%% CT_BACKENDS=riak,mysql rebar ct suites=ejabberd +get_config_backends() -> + case os:getenv("CT_BACKENDS") of + false -> all; + String -> + Backends0 = string:tokens(String, ","), + lists:map(fun(Backend) -> string:strip(Backend, both, $ ) end, Backends0) + end. + process_config_tpl(Content, []) -> Content; process_config_tpl(Content, [{Name, DefaultValue} | Rest]) ->