1: %%%------------------------------------------------------------------- 2: %%% @doc 3: %%% 4: %%% @end 5: %%%------------------------------------------------------------------- 6: -module(mongoose_service_SUITE). 7: -compile([export_all, nowarn_export_all]). 8: -include_lib("common_test/include/ct.hrl"). 9: -include_lib("eunit/include/eunit.hrl"). 10: 11: 12: all() -> 13: [ 14: starts_service, 15: ensure, 16: verify_deps, 17: start_deps, 18: module_deps, 19: misconfigured 20: ]. 21: 22: services() -> [service_a, service_b, service_c, service_d, service_e, service_f, service_g, 23: service_h, service_i, service_j, service_k, service_l]. 24: 25: dep_services() -> [service_c, service_d, service_e, service_f, service_g, service_h]. 26: 27: %% c 28: %% / \ 29: %% d e 30: %% / \ / \ 31: %% f g h 32: %% ^ | 33: %% | | 34: %% ------- 35: 36: %% i 37: %% / \ 38: %% j k <-- 39: %% \ | 40: %% l-- 41: 42: service_deps() -> 43: [ 44: {service_c, [service_d, service_e]}, 45: {service_d, [service_f, service_g]}, 46: {service_e, [service_g, service_h]}, 47: {service_h, [service_f]}, 48: % and now for something completely cicrular 49: {service_i, [service_j, service_k]}, 50: {service_k, [service_l]}, 51: {service_l, [service_k]} 52: ]. 53: 54: init_per_testcase(misconfigured, C) -> 55: mongoose_service:start(), 56: mongoose_config:set_opt(services, [{service_a, []}]), 57: meck:new(service_a, [non_strict]), 58: meck:expect(service_a, deps, fun() -> [service_b] end), 59: C; 60: 61: init_per_testcase(module_deps, C) -> 62: init_per_testcase(generic, C), 63: mongoose_config:set_opt(hosts, [<<"localhost">>]), 64: mongoose_config:set_opt(host_types, []), 65: mongoose_config:set_opt({modules, <<"localhost">>}, #{module_a => []}), 66: meck:new(module_a, [non_strict]), 67: meck:expect(module_a, deps, fun(_, _) -> [{service, service_d}, {service, service_h}] end), 68: meck:expect(module_a, start, fun(_, _) -> ok end), 69: meck:expect(module_a, stop, fun(_) -> ok end), 70: C; 71: init_per_testcase(_, C) -> 72: mongoose_service:start(), 73: ets:new(testservice, [named_table]), 74: meck:new(services(), [non_strict]), 75: lists:map(fun(S) -> 76: meck:expect(S, start, fun(_Opts) -> increment(S), done end) 77: end, services()), 78: lists:map(fun(S) -> 79: meck:expect(S, stop, fun() -> decrement(S) end) 80: end, services()), 81: mongoose_config:set_opt(services, [{Serv, []} || Serv <- services()]), 82: lists:map(fun(Serv) -> 83: meck:expect(Serv, deps, fun() -> proplists:get_value(Serv, service_deps()) end) 84: end, 85: proplists:get_keys(service_deps())), 86: C. 87: 88: end_per_testcase(misconfigured, C) -> 89: mongoose_config:unset_opt(services), 90: meck:unload(service_a), 91: C; 92: 93: end_per_testcase(module_deps, C) -> 94: mongoose_config:unset_opt(hosts), 95: mongoose_config:unset_opt(host_types), 96: mongoose_config:unset_opt({modules, <<"localhost">>}), 97: meck:unload(module_a), 98: end_per_testcase(generic, C); 99: end_per_testcase(_, C) -> 100: mongoose_config:unset_opt(services), 101: meck:unload(services()), 102: lists:foreach(fun mongoose_service:stop_service/1, services()), 103: mongoose_service:stop(), 104: C. 105: 106: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 107: %% 108: %% tests 109: %% 110: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 111: 112: starts_service(_C) -> 113: {ok, done} = mongoose_service:start_service(service_a, []), 114: {error, already_started} = mongoose_service:start_service(service_a, []), 115: ?assertEqual(1, read(service_a)), 116: {ok, done} = mongoose_service:start_service(service_b, []), 117: {error, already_started} = mongoose_service:start_service(service_b, []), 118: ?assertEqual(1, read(service_b)), 119: ok = mongoose_service:stop_service(service_a), 120: ?assertEqual(0, read(service_a)), 121: {error, not_running} = mongoose_service:stop_service(service_a), 122: ?assertEqual(0, read(service_a)), 123: ok = mongoose_service:stop_service(service_b), 124: ?assertEqual(0, read(service_b)), 125: {error, not_running} = mongoose_service:stop_service(service_b), 126: ?assertEqual(0, read(service_b)), 127: ok. 128: 129: ensure(_C) -> 130: ok = mongoose_service:ensure_loaded(service_a), 131: ok = mongoose_service:ensure_loaded(service_a), 132: ok = mongoose_service:ensure_loaded(service_a), 133: ?assertEqual(1, read(service_a)), 134: ok = mongoose_service:stop_service(service_a), 135: ?assertEqual(0, read(service_a)), 136: ok. 137: 138: verify_deps(_) -> 139: mongoose_service:check_deps(service_c), 140: mongoose_service:check_deps(service_d), 141: mongoose_service:check_deps(service_e), 142: mongoose_service:check_deps(service_f), 143: mongoose_service:check_deps(service_g), 144: mongoose_service:check_deps(service_g), 145: ?assertException(error, {circular_deps_detected, _}, mongoose_service:check_deps(service_i)), 146: mongoose_service:check_deps(service_j), 147: ?assertException(error, {circular_deps_detected, _}, mongoose_service:check_deps(service_k)), 148: ?assertException(error, {circular_deps_detected, _}, mongoose_service:check_deps(service_l)), 149: ok. 150: 151: start_deps(_) -> 152: assert_loaded([]), 153: mongoose_service:ensure_loaded(service_f), 154: assert_loaded([service_f]), 155: mongoose_service:ensure_loaded(service_d), 156: assert_loaded([service_d, service_f, service_g]), 157: mongoose_service:ensure_loaded(service_e), 158: assert_loaded([service_d, service_e, service_f, service_g, service_h]), 159: mongoose_service:ensure_loaded(service_c), 160: assert_loaded(dep_services()), 161: lists:foreach(fun mongoose_service:stop_service/1, services()), 162: assert_loaded([]), 163: mongoose_service:ensure_loaded(service_c), 164: assert_loaded(dep_services()), 165: ok. 166: 167: module_deps(_) -> 168: assert_loaded([]), 169: meck:new(gen_mod, [passthrough]), 170: meck:expect(gen_mod, is_app_running, fun(_Mooo) -> true end), 171: ?assertError({service_not_loaded, _}, mongoose_modules:start()), 172: meck:unload(gen_mod), 173: mongoose_service:ensure_loaded(service_c), 174: mongoose_modules:start(), 175: ?assert(gen_mod:is_loaded(<<"localhost">>, module_a)), 176: ok. 177: 178: misconfigured(_) -> 179: ?assertError({service_not_configured, service_b}, mongoose_service:ensure_loaded(service_a)), 180: ok. 181: 182: 183: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 184: %% 185: %% helpers 186: %% 187: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 188: 189: increment(S) -> 190: Ni = case ets:lookup(testservice, S) of 191: [{S, I}] -> I + 1; 192: [] -> 1 193: end, 194: ets:insert(testservice, {S, Ni}). 195: 196: decrement(S) -> 197: Ni = case ets:lookup(testservice, S) of 198: [{S, I}] -> I - 1; 199: [] -> -1 200: end, 201: ets:insert(testservice, {S, Ni}). 202: 203: read(S) -> 204: case ets:lookup(testservice, S) of 205: [{S, I}] -> I; 206: [] -> 0 207: end. 208: 209: assert_loaded(Loaded) -> 210: _ = [{S, mongoose_service:is_loaded(S)} || S <- services()], 211: NotLoaded = sets:to_list( 212: sets:subtract( 213: sets:from_list(dep_services()), 214: sets:from_list(Loaded))), 215: ?assert(lists:all(fun mongoose_service:is_loaded/1, Loaded)), 216: ?assert(lists:all(fun(S) -> not mongoose_service:is_loaded(S) end, NotLoaded)), 217: ok.