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