1: -module(mongoose_modules_SUITE). 2: -compile([export_all, nowarn_export_all]). 3: 4: -include_lib("eunit/include/eunit.hrl"). 5: 6: -define(MODS, [mod_a, mod_b, mod_c, mod_d]). 7: 8: -define(HOST, <<"localhost">>). 9: 10: all() -> 11: [starts_and_stops_modules, 12: ensures_module, 13: reverts_config_when_module_fails_to_start, 14: does_not_change_config_when_module_fails_to_stop, 15: replaces_modules, 16: replaces_modules_with_new_deps, 17: replaces_modules_with_old_deps, 18: replaces_modules_with_same_deps]. 19: 20: init_per_suite(C) -> 21: [mongoose_config:set_opt(Opt, Val) || {Opt, Val} <- opts()], 22: C. 23: 24: end_per_suite(_C) -> 25: [mongoose_config:unset_opt(Opt) || {Opt, _} <- opts()], 26: ok. 27: 28: init_per_testcase(_TC, C) -> 29: meck:new(gen_mod, [passthrough]), 30: meck:expect(gen_mod, start_module, fun(_, _, _) -> {ok, start_result} end), 31: meck:expect(gen_mod, stop_module, fun(_, _) -> ok end), 32: meck:new(?MODS, [non_strict]), 33: C. 34: 35: end_per_testcase(_, _C) -> 36: mongoose_config:unset_opt({modules, ?HOST}), 37: meck:unload(gen_mod), 38: meck:unload(?MODS). 39: 40: starts_and_stops_modules(_Config) -> 41: set_modules(Modules = #{mod_a => #{}, mod_b => [{opt, val}]}), 42: ok = mongoose_modules:start(), 43: check_started(maps:to_list(Modules)), 44: 45: ok = mongoose_modules:stop(), 46: check_stopped([mod_a, mod_b]). 47: 48: ensures_module(_Config) -> 49: set_modules(#{}), 50: ?assertEqual({started, start_result}, mongoose_modules:ensure_started(?HOST, mod_a, #{})), 51: ?assertEqual(#{mod_a => #{}}, get_modules()), 52: 53: ?assertEqual(already_started, mongoose_modules:ensure_started(?HOST, mod_a, #{})), 54: ?assertEqual(#{mod_a => #{}}, get_modules()), 55: 56: ?assertEqual({restarted, #{}, start_result}, 57: mongoose_modules:ensure_started(?HOST, mod_a, #{opt => val})), 58: ?assertEqual(#{mod_a => #{opt => val}}, get_modules()), 59: 60: ?assertEqual({stopped, #{opt => val}}, mongoose_modules:ensure_stopped(?HOST, mod_a)), 61: ?assertEqual(#{}, get_modules()), 62: 63: ?assertEqual(already_stopped, mongoose_modules:ensure_stopped(?HOST, mod_a)), 64: ?assertEqual(#{}, get_modules()). 65: 66: reverts_config_when_module_fails_to_start(_Config) -> 67: set_modules(#{}), 68: meck:expect(gen_mod, start_module, fun(_, _, _) -> error(something_awful) end), 69: ?assertError(something_awful, mongoose_modules:ensure_started(?HOST, mod_a, #{})), 70: ?assertEqual(#{}, get_modules()). 71: 72: does_not_change_config_when_module_fails_to_stop(_Config) -> 73: set_modules(#{mod_a => #{}}), 74: meck:expect(gen_mod, stop_module, fun(_, _) -> error(something_awful) end), 75: ?assertError(something_awful, mongoose_modules:ensure_stopped(?HOST, mod_a)), 76: ?assertEqual(#{mod_a => #{}}, get_modules()). 77: 78: replaces_modules(_Config) -> 79: set_modules(Modules = #{mod_a => #{}, mod_b => #{opt => val}, mod_c => #{}}), 80: ok = mongoose_modules:start(), 81: check_started(maps:to_list(Modules)), 82: 83: %% Stop mod_a, change opts for mod_b, do not change mod_c, start mod_d 84: NewModules = #{mod_b => #{new_opt => new_val}, mod_c => #{}, mod_d => #{}}, 85: ok = mongoose_modules:replace_modules(?HOST, [mod_a], NewModules), 86: check_stopped([mod_a, mod_b]), 87: check_not_stopped([mod_c]), 88: check_started([{mod_b, #{new_opt => new_val}}, {mod_d, #{}}]), 89: ?assertEqual(NewModules, get_modules()), 90: 91: ok = mongoose_modules:stop(), 92: check_stopped([mod_b, mod_c, mod_d]). 93: 94: replaces_modules_with_new_deps(_Config) -> 95: set_deps(#{mod_b => [{mod_c, #{}, hard}]}), 96: set_modules(Modules = #{mod_a => #{}}), 97: ok = mongoose_modules:start(), 98: check_started(maps:to_list(Modules)), 99: 100: %% Start mod_b, which depends on mod_c 101: ok = mongoose_modules:replace_modules(?HOST, [], #{mod_b => #{}}), 102: check_not_stopped([mod_a]), 103: check_started([{mod_b, #{}}, {mod_c, #{}}]), 104: ?assertEqual(Modules#{mod_b => #{}, mod_c => #{}}, get_modules()), 105: 106: ok = mongoose_modules:stop(), 107: check_stopped([mod_a, mod_b, mod_c]). 108: 109: replaces_modules_with_old_deps(_Config) -> 110: set_deps(#{mod_a => [{mod_c, #{}, hard}]}), 111: set_modules(Modules = #{mod_a => #{}, mod_c => #{}}), 112: ok = mongoose_modules:start(), 113: check_started(maps:to_list(Modules)), 114: 115: %% Stop mod_a, which depends on mod_c, and start mod_b 116: ok = mongoose_modules:replace_modules(?HOST, [mod_a], #{mod_b => #{}}), 117: check_stopped([mod_a, mod_c]), 118: check_started([{mod_b, #{}}]), 119: ?assertEqual(#{mod_b => #{}}, get_modules()), 120: 121: ok = mongoose_modules:stop(), 122: check_stopped([mod_b]). 123: 124: replaces_modules_with_same_deps(_Config) -> 125: set_deps(#{mod_a => [{mod_c, #{}, hard}], mod_b => [{mod_c, #{}, hard}]}), 126: set_modules(Modules = #{mod_a => #{}, mod_c => #{}}), 127: ok = mongoose_modules:start(), 128: check_started(maps:to_list(Modules)), 129: 130: %% Stop mod_a, and start mod_b, both depending on mod_c 131: ok = mongoose_modules:replace_modules(?HOST, [mod_a], #{mod_b => #{}}), 132: check_stopped([mod_a]), 133: check_not_stopped([mod_c]), 134: check_started([{mod_b, #{}}]), 135: ?assertEqual(#{mod_b => #{}, mod_c => #{}}, get_modules()), 136: 137: ok = mongoose_modules:stop(), 138: check_stopped([mod_b]). 139: 140: check_started(ModulesWithOpts) -> 141: lists:foreach(fun({Mod, Opts}) -> 142: ?assert(meck:called(gen_mod, start_module, [?HOST, Mod, Opts])) 143: end, ModulesWithOpts). 144: 145: check_stopped(Modules) -> 146: lists:foreach(fun(Mod) -> 147: ?assert(meck:called(gen_mod, stop_module, [?HOST, Mod])) 148: end, Modules). 149: 150: check_not_stopped(Modules) -> 151: lists:foreach(fun(Mod) -> 152: ?assertNot(meck:called(gen_mod, stop_module, [?HOST, Mod])) 153: end, Modules). 154: 155: check_modules(ExpectedModules) -> 156: ?assertEqual(ExpectedModules, gen_mod:loaded_modules_with_opts(?HOST)). 157: 158: opts() -> 159: [{hosts, [?HOST]}, 160: {host_types, []}]. 161: 162: set_modules(Modules) -> 163: mongoose_config:set_opt({modules, ?HOST}, Modules). 164: 165: get_modules() -> 166: mongoose_config:get_opt({modules, ?HOST}). 167: 168: set_deps(DepsMap) -> 169: maps:fold(fun(Mod, Deps, _) -> meck:expect(Mod, deps, fun(_, _) -> Deps end) end, 170: undefined, DepsMap).