diff options
Diffstat (limited to 'src/couch/src/test_util.erl')
-rw-r--r-- | src/couch/src/test_util.erl | 429 |
1 files changed, 0 insertions, 429 deletions
diff --git a/src/couch/src/test_util.erl b/src/couch/src/test_util.erl deleted file mode 100644 index 9f6d758c5..000000000 --- a/src/couch/src/test_util.erl +++ /dev/null @@ -1,429 +0,0 @@ -% 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. - --module(test_util). - --include_lib("couch/include/couch_eunit.hrl"). --include("couch_db.hrl"). --include("couch_db_int.hrl"). --include("couch_bt_engine.hrl"). - --export([init_code_path/0]). --export([source_file/1, build_file/1]). -%% -export([run/2]). - --export([start_couch/0, start_couch/1, start_couch/2, stop_couch/0, stop_couch/1]). --export([start_config/1, stop_config/1]). --export([start_applications/1, stop_applications/1]). - --export([stop_sync/1, stop_sync/2, stop_sync/3]). - --export([stop_sync_throw/2, stop_sync_throw/3, stop_sync_throw/4]). - --export([with_process_restart/1, with_process_restart/2, with_process_restart/3]). --export([wait_process/1, wait_process/2]). --export([wait/1, wait/2, wait/3]). --export([wait_value/2, wait_other_value/2]). --export([with_processes_restart/2, with_processes_restart/4]). --export([with_couch_server_restart/1]). - --export([start/1, start/2, start/3, stop/1]). - --export([fake_db/1]). - --record(test_context, {mocked = [], started = [], module}). - --define(DEFAULT_APPS, [inets, ibrowse, ssl, config, couch_epi, couch_event, couch]). - -srcdir() -> - code:priv_dir(couch) ++ "/../../". - -builddir() -> - code:priv_dir(couch) ++ "/../../../". - -init_code_path() -> - Paths = [ - "couchdb", - "jiffy", - "ibrowse", - "mochiweb", - "snappy" - ], - lists:foreach( - fun(Name) -> - code:add_patha(filename:join([builddir(), "src", Name])) - end, - Paths - ). - -source_file(Name) -> - filename:join([srcdir(), Name]). - -build_file(Name) -> - filename:join([builddir(), Name]). - -start_couch() -> - start_couch(?CONFIG_CHAIN, []). - -start_couch(ExtraApps) -> - start_couch(?CONFIG_CHAIN, ExtraApps). - -start_couch(IniFiles, ExtraApps) -> - load_applications_with_stats(), - ok = application:set_env(config, ini_files, IniFiles), - Apps = start_applications(?DEFAULT_APPS ++ ExtraApps), - ok = config:delete("compactions", "_default", false), - #test_context{started = Apps}. - -stop_couch() -> - ok = stop_applications(?DEFAULT_APPS). - -stop_couch(#test_context{started = Apps}) -> - stop_applications(Apps); -stop_couch(_) -> - stop_couch(). - -with_couch_server_restart(Fun) -> - Servers = couch_server:names(), - test_util:with_processes_restart(Servers, Fun). - -start_applications(Apps) -> - StartOrder = calculate_start_order(Apps), - start_applications(StartOrder, []). - -start_applications([], Acc) -> - lists:reverse(Acc); -start_applications([App | Apps], Acc) when App == kernel; App == stdlib -> - start_applications(Apps, Acc); -start_applications([App | Apps], Acc) -> - case application:start(App) of - {error, {already_started, crypto}} -> - start_applications(Apps, [crypto | Acc]); - {error, {already_started, App}} -> - io:format(standard_error, "Application ~s was left running!~n", [App]), - application:stop(App), - start_applications([App | Apps], Acc); - {error, Reason} -> - io:format(standard_error, "Cannot start application '~s', reason ~p~n", [App, Reason]), - throw({error, {cannot_start, App, Reason}}); - ok -> - start_applications(Apps, [App | Acc]) - end. - -stop_applications(Apps) -> - [application:stop(App) || App <- lists:reverse(Apps)], - ok. - -start_config(Chain) -> - case config:start_link(Chain) of - {ok, Pid} -> - {ok, Pid}; - {error, {already_started, OldPid}} -> - ok = stop_config(OldPid), - start_config(Chain) - end. - -stop_config(Pid) -> - Timeout = 1000, - case stop_sync(Pid, fun() -> config:stop() end, Timeout) of - timeout -> - throw({timeout_error, config_stop}); - _Else -> - ok - end. - -stop_sync(Name) -> - stop_sync(Name, shutdown). -stop_sync(Name, Reason) -> - stop_sync(Name, Reason, 5000). - -stop_sync(Name, Reason, Timeout) when is_atom(Name) -> - stop_sync(whereis(Name), Reason, Timeout); -stop_sync(Pid, Reason, Timeout) when is_atom(Reason) and is_pid(Pid) -> - stop_sync(Pid, fun() -> exit(Pid, Reason) end, Timeout); -stop_sync(Pid, Fun, Timeout) when is_function(Fun) and is_pid(Pid) -> - MRef = erlang:monitor(process, Pid), - try - begin - catch unlink(Pid), - Res = (catch Fun()), - receive - {'DOWN', MRef, _, _, _} -> - Res - after Timeout -> - timeout - end - end - after - erlang:demonitor(MRef, [flush]) - end; -stop_sync(_, _, _) -> - error(badarg). - -stop_sync_throw(Name, Error) -> - stop_sync_throw(Name, shutdown, Error). -stop_sync_throw(Name, Reason, Error) -> - stop_sync_throw(Name, Reason, Error, 5000). - -stop_sync_throw(Pid, Fun, Error, Timeout) -> - case stop_sync(Pid, Fun, Timeout) of - timeout -> - throw(Error); - Else -> - Else - end. - -with_process_restart(Name) -> - {Pid, true} = with_process_restart( - Name, fun() -> exit(whereis(Name), shutdown) end - ), - Pid. - -with_process_restart(Name, Fun) -> - with_process_restart(Name, Fun, 5000). - -with_process_restart(Name, Fun, Timeout) -> - Res = stop_sync(Name, Fun), - case wait_process(Name, Timeout) of - timeout -> - timeout; - Pid -> - {Pid, Res} - end. - -wait_process(Name) -> - wait_process(Name, 5000). -wait_process(Name, Timeout) -> - wait( - fun() -> - case whereis(Name) of - undefined -> - wait; - Pid -> - Pid - end - end, - Timeout - ). - -wait(Fun) -> - wait(Fun, 5000, 50). - -wait(Fun, Timeout) -> - wait(Fun, Timeout, 50). - -wait(Fun, Timeout, Delay) -> - Now = now_us(), - wait(Fun, Timeout * 1000, Delay, Now, Now). - -wait(_Fun, Timeout, _Delay, Started, Prev) when Prev - Started > Timeout -> - timeout; -wait(Fun, Timeout, Delay, Started, _Prev) -> - case Fun() of - wait -> - ok = timer:sleep(Delay), - wait(Fun, Timeout, Delay, Started, now_us()); - Else -> - Else - end. - -wait_value(Fun, Value) -> - wait(fun() -> - case Fun() of - Value -> Value; - _ -> wait - end - end). - -wait_other_value(Fun, Value) -> - wait(fun() -> - case Fun() of - Value -> wait; - Other -> Other - end - end). - -with_processes_restart(Processes, Fun) -> - with_processes_restart(Processes, Fun, 5000, 50). - -with_processes_restart(Names, Fun, Timeout, Delay) -> - Processes = lists:foldl( - fun(Name, Acc) -> - [{Name, whereis(Name)} | Acc] - end, - [], - Names - ), - [catch unlink(Pid) || {_, Pid} <- Processes], - Res = (catch Fun()), - {wait_start(Processes, Timeout, Delay), Res}. - -wait_start(Processses, TimeoutInSec, Delay) -> - Now = now_us(), - wait_start(Processses, TimeoutInSec * 1000, Delay, Now, Now, #{}). - -wait_start(_, Timeout, _Delay, Started, Prev, _) when Prev - Started > Timeout -> - timeout; -wait_start([], _Timeout, _Delay, _Started, _Prev, Res) -> - Res; -wait_start([{Name, Pid} | Rest] = Processes, Timeout, Delay, Started, _Prev, Res) -> - case whereis(Name) of - NewPid when is_pid(NewPid) andalso NewPid =/= Pid -> - wait_start(Rest, Timeout, Delay, Started, now_us(), maps:put(Name, NewPid, Res)); - _ -> - ok = timer:sleep(Delay), - wait_start(Processes, Timeout, Delay, Started, now_us(), Res) - end. - -start(Module) -> - start(Module, [], []). - -start(Module, ExtraApps) -> - start(Module, ExtraApps, []). - -start(Module, ExtraApps, Options) -> - Apps = start_applications([config, couch_log, ioq, couch_epi | ExtraApps]), - ToMock = [config, couch_stats] -- proplists:get_value(dont_mock, Options, []), - mock(ToMock), - #test_context{module = Module, mocked = ToMock, started = Apps}. - -stop(#test_context{mocked = Mocked, started = Apps}) -> - meck:unload(Mocked), - stop_applications(Apps). - -fake_db(Fields0) -> - {ok, Db, Fields} = maybe_set_engine(Fields0), - Indexes = lists:zip( - record_info(fields, db), - lists:seq(2, record_info(size, db)) - ), - lists:foldl( - fun({FieldName, Value}, Acc) -> - Idx = couch_util:get_value(FieldName, Indexes), - setelement(Idx, Acc, Value) - end, - Db, - Fields - ). - -maybe_set_engine(Fields0) -> - case lists:member(engine, Fields0) of - true -> - {ok, #db{}, Fields0}; - false -> - {ok, Header, Fields} = get_engine_header(Fields0), - Db = #db{engine = {couch_bt_engine, #st{header = Header}}}, - {ok, Db, Fields} - end. - -get_engine_header(Fields) -> - Keys = [ - disk_version, - update_seq, - unused, - id_tree_state, - seq_tree_state, - local_tree_state, - purge_seq, - purged_docs, - security_ptr, - revs_limit, - uuid, - epochs, - compacted_seq - ], - {HeadFields, RestFields} = lists:partition( - fun({K, _}) -> lists:member(K, Keys) end, Fields - ), - Header0 = couch_bt_engine_header:new(), - Header = couch_bt_engine_header:set(Header0, HeadFields), - {ok, Header, RestFields}. - -now_us() -> - {MegaSecs, Secs, MicroSecs} = os:timestamp(), - (MegaSecs * 1000000 + Secs) * 1000000 + MicroSecs. - -mock(Modules) when is_list(Modules) -> - [mock(Module) || Module <- Modules]; -mock(config) -> - meck:new(config, [passthrough]), - meck:expect(config, get, fun(_, _) -> undefined end), - meck:expect(config, get, fun(_, _, Default) -> Default end), - ok; -mock(couch_stats) -> - meck:new(couch_stats, [passthrough]), - meck:expect(couch_stats, increment_counter, fun(_) -> ok end), - meck:expect(couch_stats, increment_counter, fun(_, _) -> ok end), - meck:expect(couch_stats, decrement_counter, fun(_) -> ok end), - meck:expect(couch_stats, decrement_counter, fun(_, _) -> ok end), - meck:expect(couch_stats, update_histogram, fun(_, _) -> ok end), - meck:expect(couch_stats, update_gauge, fun(_, _) -> ok end), - ok. - -load_applications_with_stats() -> - Wildcard = filename:join([?BUILDDIR(), "src/*/priv/stats_descriptions.cfg"]), - [application:load(stats_file_to_app(File)) || File <- filelib:wildcard(Wildcard)], - ok. - -stats_file_to_app(File) -> - [_Desc, _Priv, App | _] = lists:reverse(filename:split(File)), - erlang:list_to_atom(App). - -calculate_start_order(Apps) -> - AllApps = calculate_start_order(sort_apps(Apps), []), - % AllApps may not be the same list as Apps if we - % loaded any dependencies. We recurse here when - % that changes so that our sort_apps function has - % a global view of all applications to start. - case lists:usort(AllApps) == lists:usort(Apps) of - true -> AllApps; - false -> calculate_start_order(AllApps) - end. - -calculate_start_order([], StartOrder) -> - lists:reverse(StartOrder); -calculate_start_order([App | RestApps], StartOrder) -> - NewStartOrder = load_app_deps(App, StartOrder), - calculate_start_order(RestApps, NewStartOrder). - -load_app_deps(App, StartOrder) -> - case lists:member(App, StartOrder) of - true -> - StartOrder; - false -> - case application:load(App) of - ok -> ok; - {error, {already_loaded, App}} -> ok - end, - {ok, Apps} = application:get_key(App, applications), - Deps = - case App of - kernel -> Apps; - stdlib -> Apps; - _ -> lists:usort([kernel, stdlib | Apps]) - end, - NewStartOrder = lists:foldl( - fun(Dep, Acc) -> - load_app_deps(Dep, Acc) - end, - StartOrder, - Deps - ), - [App | NewStartOrder] - end. - -sort_apps(Apps) -> - Weighted = [weight_app(App) || App <- Apps], - element(2, lists:unzip(lists:sort(Weighted))). - -weight_app(couch_log) -> {0.0, couch_log}; -weight_app(Else) -> {1.0, Else}. |