Rewrite test suite to test new functionality
Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch-epi/repo Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch-epi/commit/baa9a355 Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch-epi/tree/baa9a355 Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch-epi/diff/baa9a355 Branch: refs/heads/master Commit: baa9a3556097ff16a4390026495ba601cc10d0cd Parents: 67612a6 Author: ILYA Khlopotov <[email protected]> Authored: Wed Jun 24 14:14:37 2015 -0700 Committer: ILYA Khlopotov <[email protected]> Committed: Wed Jun 24 15:13:50 2015 -0700 ---------------------------------------------------------------------- test/couch_epi_tests.erl | 280 ++++++++++++++++++++++++++++-------------- 1 file changed, 185 insertions(+), 95 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/couchdb-couch-epi/blob/baa9a355/test/couch_epi_tests.erl ---------------------------------------------------------------------- diff --git a/test/couch_epi_tests.erl b/test/couch_epi_tests.erl index bba2ae6..227a79f 100644 --- a/test/couch_epi_tests.erl +++ b/test/couch_epi_tests.erl @@ -19,9 +19,7 @@ -export([notify_cb/5, save/3]). --record(ctx, { - file, data_handle, data_source, - functions_handle, functions_source, kv}). +-record(ctx, {file, handle, pid, kv, key}). -define(TIMEOUT, 5000). @@ -50,87 +48,146 @@ throw(check_error). "). +-define(DATA_MODULE1(Name), " + -export([data/0]). + + data() -> + [ + {[complex, key, 1], [ + {type, counter}, + {desc, foo} + ]} + ]. +"). + +-define(DATA_MODULE2(Name), " + -export([data/0]). + + data() -> + [ + {[complex, key, 2], [ + {type, counter}, + {desc, bar} + ]}, + {[complex, key, 1], [ + {type, counter}, + {desc, updated_foo} + ]} + ]. +"). notify_cb(App, Key, OldData, Data, KV) -> save(KV, is_called, {App, Key, OldData, Data}). -setup() -> + +setup(couch_epi_data_source) -> error_logger:tty(false), Key = {test_app, descriptions}, File = ?tempfile(), {ok, _} = file:copy(?DATA_FILE1, File), application:start(couch_epi), - {ok, DataPid} = couch_epi_data_source:start_link( + {ok, Pid} = couch_epi_data_source:start_link( test_app, {epi_key, Key}, {file, File}, [{interval, 100}]), - ok = couch_epi_data_source:wait(DataPid), + ok = couch_epi_data_source:wait(Pid), + KV = state_storage(), + #ctx{ + file = File, + key = Key, + handle = couch_epi:get_handle(Key), + kv = KV, + pid = Pid}; +setup(couch_epi_data) -> + error_logger:tty(false), + Key = {test_app, descriptions}, + application:start(couch_epi), + ok = generate_module(provider, ?DATA_MODULE1(provider)), + + {ok, Pid} = couch_epi_data:start_link( + test_app, {epi_key, Key}, provider, []), + ok = couch_epi_data:wait(Pid), + KV = state_storage(), + #ctx{ + key = Key, + handle = couch_epi:get_handle(Key), + kv = KV, + pid = Pid}; +setup(couch_epi_functions) -> + Key = my_service, + error_logger:tty(false), + + application:start(couch_epi), ok = generate_module(provider1, ?MODULE1(provider1)), ok = generate_module(provider2, ?MODULE2(provider2)), - {ok, FunctionsPid} = couch_epi_functions:start_link( - test_app, {epi_key, my_service}, {modules, [provider1, provider2]}, + {ok, Pid} = couch_epi_functions:start_link( + test_app, {epi_key, Key}, {modules, [provider1, provider2]}, [{interval, 100}]), - ok = couch_epi_functions:wait(FunctionsPid), + ok = couch_epi_functions:wait(Pid), KV = state_storage(), #ctx{ - file = File, - data_handle = couch_epi:get_handle(Key), - functions_handle = couch_epi:get_handle(my_service), + key = Key, + handle = couch_epi:get_handle(Key), kv = KV, - data_source = DataPid, - functions_source = FunctionsPid}. - + pid = Pid}; setup(_Opts) -> - setup(). + setup(couch_epi_functions). -teardown(_, #ctx{} = Ctx) -> +teardown(Module, #ctx{pid = Pid} = Ctx) when is_atom(Module) -> + Module:stop(Pid), + teardown(Ctx); +teardown(_Opts, #ctx{pid = Pid} = Ctx) -> + couch_epi_functions:stop(Pid), teardown(Ctx). -teardown(#ctx{data_source = DataPid, - functions_source = FunctionsPid, - kv = KV, file = File}) -> +teardown(#ctx{file = File} = Ctx) when File /= undefined -> file:delete(File), - couch_epi_data_source:stop(DataPid), - couch_epi_functions:stop(FunctionsPid), - catch meck:unload(compile), + teardown(Ctx#ctx{file = undefined}); +teardown(#ctx{kv = KV}) -> call(KV, stop), application:stop(couch_epi), ok. +upgrade_release(Pid, Module) -> + sys:suspend(Pid), + 'ok' = sys:change_code(Pid, Module, 'undefined', []), + sys:resume(Pid), + ok. + epi_config_update_test_() -> Funs = [ fun ensure_notified_when_changed/2, fun ensure_not_notified_when_no_change/2, fun ensure_not_notified_when_unsubscribed/2 ], + Modules= [ + couch_epi_data, + couch_epi_data_source, + couch_epi_functions + ], { "config update tests", - { - foreach, - fun setup/0, - fun teardown/1, - [make_case("Check notifications for: ", [module, file], Funs)] - } + [make_case("Check notifications for: ", Modules, Funs)] }. epi_data_source_test_() -> + Funs = [ + fun check_dump/2, + fun check_get/2, + fun check_get_value/2, + fun check_by_key/2, + fun check_by_source/2, + fun check_keys/2, + fun check_subscribers/2 + ], + Modules= [ + couch_epi_data, + couch_epi_data_source + ], { - "epi data_source tests", - { - foreach, - fun setup/0, - fun teardown/1, - [ - fun check_dump/1, - fun check_get/1, - fun check_get_value/1, - fun check_by_key/1, - fun check_by_source/1, - fun check_keys/1, - fun check_subscribers/1 - ] - } + "epi data API tests", + [make_case("Check query API for: ", Modules, Funs)] }. @@ -139,7 +196,7 @@ epi_apply_test_() -> "epi dispatch tests", { foreach, - fun setup/0, + fun() -> setup(couch_epi_functions) end, fun teardown/1, [ fun check_pipe/1, @@ -150,16 +207,35 @@ epi_apply_test_() -> } }. + epi_subscription_test_() -> + Funs = [ + fun ensure_unsubscribe_when_caller_die/2 + ], + Modules= [ + couch_epi_data, + couch_epi_data_source, + couch_epi_functions + ], { "epi subscription tests", + [make_case("Check subscription API for: ", Modules, Funs)] + }. + + +epi_reload_test_() -> + Modules= [ + couch_epi_data, + couch_epi_data_source, + couch_epi_functions + ], + { + "epi reload tests", { - foreach, - fun setup/0, - fun teardown/1, - [ - fun ensure_unsubscribe_when_caller_die/1 - ] + foreachx, + fun setup/1, + fun teardown/2, + [{M, fun ensure_reloaded/2} || M <- Modules] } }. @@ -191,11 +267,22 @@ valid_options_permutations() -> [concurrent, ignore_errors] ]. -ensure_notified_when_changed(file, #ctx{file = File} = Ctx) -> +ensure_notified_when_changed(couch_epi_functions, #ctx{key = Key} = Ctx) -> + ?_test(begin + subscribe(Ctx, test_app, Key), + update(couch_epi_functions, Ctx), + timer:sleep(200), + Result = get(Ctx, is_called), + Expected = {test_app, Key, + {modules, [provider1, provider2]}, + {modules, [provider1, provider2]}}, + ?assertMatch({ok, Expected}, Result), + ok + end); +ensure_notified_when_changed(Module, #ctx{key = Key} = Ctx) -> ?_test(begin - Key = {test_app, descriptions}, subscribe(Ctx, test_app, Key), - update(file, Ctx), + update(Module, Ctx), timer:sleep(200), ExpectedData = lists:usort([ {[complex, key, 1], [{type, counter}, {desc, updated_foo}]}, @@ -208,84 +295,68 @@ ensure_notified_when_changed(file, #ctx{file = File} = Ctx) -> ?assertMatch( [{[complex, key, 1], [{type, counter}, {desc, foo}]}], lists:usort(OldData)) - end); -ensure_notified_when_changed(module, #ctx{file = File} = Ctx) -> - ?_test(begin - subscribe(Ctx, test_app, my_service), - update(module, Ctx), - timer:sleep(200), - Result = get(Ctx, is_called), - Expected = {test_app, my_service, - {modules, [provider1, provider2]}, - {modules, [provider1, provider2]}}, - ?assertMatch({ok, Expected}, Result), - ok end). -ensure_not_notified_when_no_change(Case, #ctx{} = Ctx) -> +ensure_not_notified_when_no_change(_Module, #ctx{key = Key} = Ctx) -> ?_test(begin - Key = {test_app, descriptions}, subscribe(Ctx, test_app, Key), timer:sleep(200), ?assertMatch(error, get(Ctx, is_called)) end). -ensure_not_notified_when_unsubscribed(Case, #ctx{file = File} = Ctx) -> +ensure_not_notified_when_unsubscribed(Module, #ctx{key = Key} = Ctx) -> ?_test(begin - Key = {test_app, descriptions}, SubscriptionId = subscribe(Ctx, test_app, Key), couch_epi:unsubscribe(SubscriptionId), timer:sleep(100), - update(Case, Ctx), + update(Module, Ctx), timer:sleep(200), ?assertMatch(error, get(Ctx, is_called)) end). -ensure_apply_is_called(Opts, #ctx{functions_handle = Handle, kv = KV} = Ctx) -> +ensure_apply_is_called(Opts, #ctx{handle = Handle, kv = KV, key = Key} = Ctx) -> ?_test(begin - couch_epi:apply(Handle, my_service, inc, [KV, 2], Opts), + couch_epi:apply(Handle, Key, inc, [KV, 2], Opts), maybe_wait(Opts), ?assertMatch({ok, _}, get(Ctx, inc1)), ?assertMatch({ok, _}, get(Ctx, inc2)), ok end). -check_pipe(#ctx{functions_handle = Handle, kv = KV}) -> +check_pipe(#ctx{handle = Handle, kv = KV, key = Key}) -> ?_test(begin - Result = couch_epi:apply(Handle, my_service, inc, [KV, 2], [pipe]), + Result = couch_epi:apply(Handle, Key, inc, [KV, 2], [pipe]), ?assertMatch([KV, 4], Result), ok end). -check_broken_pipe(#ctx{functions_handle = Handle, kv = KV} = Ctx) -> +check_broken_pipe(#ctx{handle = Handle, kv = KV, key = Key} = Ctx) -> ?_test(begin - Result = couch_epi:apply(Handle, my_service, fail, [KV, 2], [pipe, ignore_errors]), + Result = couch_epi:apply(Handle, Key, fail, [KV, 2], [pipe, ignore_errors]), ?assertMatch([KV, 3], Result), ?assertMatch([3, check_error], pipe_state(Ctx)), ok end). -ensure_fail_pipe(#ctx{functions_handle = Handle, kv = KV}) -> +ensure_fail_pipe(#ctx{handle = Handle, kv = KV, key = Key}) -> ?_test(begin ?assertThrow(check_error, - couch_epi:apply(Handle, my_service, fail, [KV, 2], [pipe])), + couch_epi:apply(Handle, Key, fail, [KV, 2], [pipe])), ok end). -ensure_fail(#ctx{functions_handle = Handle, kv = KV}) -> +ensure_fail(#ctx{handle = Handle, kv = KV, key = Key}) -> ?_test(begin ?assertThrow(check_error, - couch_epi:apply(Handle, my_service, fail, [KV, 2], [])), + couch_epi:apply(Handle, Key, fail, [KV, 2], [])), ok end). -ensure_unsubscribe_when_caller_die(#ctx{} = Ctx) -> +ensure_unsubscribe_when_caller_die(_Module, #ctx{key = Key} = Ctx) -> ?_test(begin - Key = {test_app, descriptions}, spawn(fun() -> subscribe(Ctx, test_app, Key) end), - %%update(file, Ctx), timer:sleep(200), ?assertMatch(error, get(Ctx, is_called)) end). @@ -295,28 +366,28 @@ pipe_state(Ctx) -> Trace = [get(Ctx, inc1), get(Ctx, inc2)], lists:usort([State || {ok, State} <- Trace]). -check_dump(#ctx{data_handle = Handle}) -> +check_dump(_Module, #ctx{handle = Handle}) -> ?_test(begin ?assertMatch( [[{type, counter}, {desc, foo}]], couch_epi:dump(Handle)) end). -check_get(#ctx{data_handle = Handle}) -> +check_get(_Module, #ctx{handle = Handle}) -> ?_test(begin ?assertMatch( [[{type, counter}, {desc, foo}]], couch_epi:get(Handle, [complex,key, 1])) end). -check_get_value(#ctx{data_handle = Handle}) -> +check_get_value(_Module, #ctx{handle = Handle}) -> ?_test(begin ?assertMatch( [{type, counter}, {desc, foo}], couch_epi:get_value(Handle, test_app, [complex,key, 1])) end). -check_by_key(#ctx{data_handle = Handle}) -> +check_by_key(_Module, #ctx{handle = Handle}) -> ?_test(begin ?assertMatch( [{[complex, key, 1], @@ -327,7 +398,7 @@ check_by_key(#ctx{data_handle = Handle}) -> couch_epi:by_key(Handle, [complex, key, 1])) end). -check_by_source(#ctx{data_handle = Handle}) -> +check_by_source(_Module, #ctx{handle = Handle}) -> ?_test(begin ?assertMatch( [{test_app, @@ -338,13 +409,24 @@ check_by_source(#ctx{data_handle = Handle}) -> couch_epi:by_source(Handle, test_app)) end). -check_keys(#ctx{data_handle = Handle}) -> +check_keys(_Module, #ctx{handle = Handle}) -> ?_assertMatch([[complex,key,1]], couch_epi:keys(Handle)). -check_subscribers(#ctx{data_handle = Handle}) -> +check_subscribers(_Module, #ctx{handle = Handle}) -> ?_assertMatch([test_app], couch_epi:subscribers(Handle)). +ensure_reloaded(Module, #ctx{pid = Pid, key = Key} = Ctx) -> + ?_test(begin + subscribe(Ctx, test_app, Key), + update_definitions(Module, Ctx), + Module:reload(Pid), + timer:sleep(200), + Result = get(Ctx, is_called), + ?assertNotMatch(error, Result) + end). + + %% ------------------------------------------------------------------ %% Internal Function Definitions %% ------------------------------------------------------------------ @@ -353,11 +435,19 @@ generate_module(Name, Body) -> Tokens = couch_epi_codegen:scan(Body), couch_epi_codegen:generate(Name, Tokens). -update(module, #ctx{}) -> - ok = generate_module(provider1, ?MODULE2(provider1)); -update(file, #ctx{file = File}) -> +update(Module, #ctx{pid = Pid} = Ctx) -> + update_definitions(Module, Ctx), + upgrade_release(Pid, Module). + +update_definitions(couch_epi_data_source, #ctx{file = File}) -> {ok, _} = file:copy(?DATA_FILE2, File), - ok. + ok; +update_definitions(couch_epi_data, #ctx{}) -> + ok = generate_module(provider, ?DATA_MODULE2(provider)); +update_definitions(couch_epi_functions, #ctx{}) -> + ok = generate_module(provider1, ?MODULE2(provider1)). + + subscribe(#ctx{kv = Kv}, App, Key) -> {ok, Pid} = couch_epi:subscribe(App, Key, ?MODULE, notify_cb, Kv),
