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: gen_mod:start(), 65: meck:new(module_a, [non_strict]), 66: meck:expect(module_a, deps, fun(_, _) -> [{service, service_d}, {service, service_h}] end), 67: meck:expect(module_a, start, fun(_, _) -> ok end), 68: meck:expect(module_a, stop, fun(_) -> ok end), 69: C; 70: init_per_testcase(_, C) -> 71: mongoose_service:start(), 72: ets:new(testservice, [named_table]), 73: meck:new(services(), [non_strict]), 74: lists:map(fun(S) -> 75: meck:expect(S, start, fun(_Opts) -> increment(S), done end) 76: end, services()), 77: lists:map(fun(S) -> 78: meck:expect(S, stop, fun() -> decrement(S) end) 79: end, services()), 80: mongoose_config:set_opt(services, [{Serv, []} || Serv <- services()]), 81: lists:map(fun(Serv) -> 82: meck:expect(Serv, deps, fun() -> proplists:get_value(Serv, service_deps()) end) 83: end, 84: proplists:get_keys(service_deps())), 85: C. 86: 87: end_per_testcase(misconfigured, C) -> 88: mongoose_config:unset_opt(services), 89: meck:unload(service_a), 90: C; 91: 92: end_per_testcase(module_deps, C) -> 93: mongoose_config:unset_opt(hosts), 94: meck:unload(module_a), 95: end_per_testcase(generic, C); 96: end_per_testcase(_, C) -> 97: mongoose_config:unset_opt(services), 98: meck:unload(services()), 99: lists:foreach(fun mongoose_service:stop_service/1, services()), 100: mongoose_service:stop(), 101: C. 102: 103: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 104: %% 105: %% tests 106: %% 107: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 108: 109: starts_service(_C) -> 110: {ok, done} = mongoose_service:start_service(service_a, []), 111: {error, already_started} = mongoose_service:start_service(service_a, []), 112: ?assertEqual(1, read(service_a)), 113: {ok, done} = mongoose_service:start_service(service_b, []), 114: {error, already_started} = mongoose_service:start_service(service_b, []), 115: ?assertEqual(1, read(service_b)), 116: ok = mongoose_service:stop_service(service_a), 117: ?assertEqual(0, read(service_a)), 118: {error, not_running} = mongoose_service:stop_service(service_a), 119: ?assertEqual(0, read(service_a)), 120: ok = mongoose_service:stop_service(service_b), 121: ?assertEqual(0, read(service_b)), 122: {error, not_running} = mongoose_service:stop_service(service_b), 123: ?assertEqual(0, read(service_b)), 124: ok. 125: 126: ensure(_C) -> 127: ok = mongoose_service:ensure_loaded(service_a), 128: ok = mongoose_service:ensure_loaded(service_a), 129: ok = mongoose_service:ensure_loaded(service_a), 130: ?assertEqual(1, read(service_a)), 131: ok = mongoose_service:stop_service(service_a), 132: ?assertEqual(0, read(service_a)), 133: ok. 134: 135: verify_deps(_) -> 136: mongoose_service:check_deps(service_c), 137: mongoose_service:check_deps(service_d), 138: mongoose_service:check_deps(service_e), 139: mongoose_service:check_deps(service_f), 140: mongoose_service:check_deps(service_g), 141: mongoose_service:check_deps(service_g), 142: ?assertException(error, {circular_deps_detected, _}, mongoose_service:check_deps(service_i)), 143: mongoose_service:check_deps(service_j), 144: ?assertException(error, {circular_deps_detected, _}, mongoose_service:check_deps(service_k)), 145: ?assertException(error, {circular_deps_detected, _}, mongoose_service:check_deps(service_l)), 146: ok. 147: 148: start_deps(_) -> 149: assert_loaded([]), 150: mongoose_service:ensure_loaded(service_f), 151: assert_loaded([service_f]), 152: mongoose_service:ensure_loaded(service_d), 153: assert_loaded([service_d, service_f, service_g]), 154: mongoose_service:ensure_loaded(service_e), 155: assert_loaded([service_d, service_e, service_f, service_g, service_h]), 156: mongoose_service:ensure_loaded(service_c), 157: assert_loaded(dep_services()), 158: lists:foreach(fun mongoose_service:stop_service/1, services()), 159: assert_loaded([]), 160: mongoose_service:ensure_loaded(service_c), 161: assert_loaded(dep_services()), 162: ok. 163: 164: module_deps(_) -> 165: assert_loaded([]), 166: meck:new(gen_mod, [passthrough]), 167: meck:expect(gen_mod, is_app_running, fun(_Mooo) -> true end), 168: ?assertError({service_not_loaded, _}, gen_mod:start_module(<<"localhost">>, module_a, [])), 169: meck:unload(gen_mod), 170: mongoose_service:ensure_loaded(service_c), 171: gen_mod_deps:start_modules(<<"localhost">>, [{module_a, []}]), 172: ?assert(gen_mod:is_loaded(<<"localhost">>, module_a)), 173: ok. 174: 175: misconfigured(_) -> 176: ?assertError({service_not_configured, service_b}, mongoose_service:ensure_loaded(service_a)), 177: ok. 178: 179: 180: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 181: %% 182: %% helpers 183: %% 184: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 185: 186: increment(S) -> 187: Ni = case ets:lookup(testservice, S) of 188: [{S, I}] -> I + 1; 189: [] -> 1 190: end, 191: ets:insert(testservice, {S, Ni}). 192: 193: decrement(S) -> 194: Ni = case ets:lookup(testservice, S) of 195: [{S, I}] -> I - 1; 196: [] -> -1 197: end, 198: ets:insert(testservice, {S, Ni}). 199: 200: read(S) -> 201: case ets:lookup(testservice, S) of 202: [{S, I}] -> I; 203: [] -> 0 204: end. 205: 206: assert_loaded(Loaded) -> 207: _ = [{S, mongoose_service:is_loaded(S)} || S <- services()], 208: NotLoaded = sets:to_list( 209: sets:subtract( 210: sets:from_list(dep_services()), 211: sets:from_list(Loaded))), 212: ?assert(lists:all(fun mongoose_service:is_loaded/1, Loaded)), 213: ?assert(lists:all(fun(S) -> not mongoose_service:is_loaded(S) end, NotLoaded)), 214: ok.