1: -module(ejabberd_hooks_SUITE). 2: -compile([export_all, nowarn_export_all]). 3: 4: -define(HOST, <<"localhost">>). 5: 6: all() -> 7: [ 8: a_module_fun_can_be_added, 9: a_module_fun_can_be_removed, 10: 11: hooks_run_launches_nullary_fun, 12: hooks_run_launches_unary_fun, 13: hooks_run_ignores_different_arity_funs, 14: hooks_run_stops_when_fun_returns_stop, 15: 16: hooks_run_fold_folds_with_unary_fun, 17: hooks_run_fold_folds_with_binary_fun, 18: hooks_run_fold_passes_acc_along, 19: hooks_run_fold_stops_when_fun_returns_stop, 20: hooks_run_fold_preserves_order, 21: 22: error_in_run_fold_is_ignored, 23: throw_in_run_fold_is_ignored, 24: exit_in_run_fold_is_ignored 25: ]. 26: 27: init_per_suite(C) -> 28: application:ensure_all_started(exometer_core), 29: mongoose_config:set_opt(all_metrics_are_global, false), 30: C. 31: 32: end_per_suite(_C) -> 33: mongoose_config:unset_opt(all_metrics_are_global), 34: application:stop(exometer_core). 35: 36: a_module_fun_can_be_added(_) -> 37: given_hooks_started(), 38: given_module(hook_mod, fun_a, fun(_) -> ok end), 39: 40: % when 41: ejabberd_hooks:add(test_run_hook, ?HOST, hook_mod, fun_a, 1), 42: 43: FunEjabberdHooksWrapper = fun ejabberd_hooks:gen_hook_fn_wrapper/3, 44: % then 45: [{{test_run_hook, <<"localhost">>}, 46: [{hook_handler, 1, FunEjabberdHooksWrapper, 47: #{function := fun_a, module := hook_mod}}]}] = get_hooks(). 48: 49: a_module_fun_can_be_removed(_) -> 50: given_hooks_started(), 51: given_module(hook_mod, fun_nullary, fun() -> success0 end), 52: given_hook_added(test_run_hook, hook_mod, fun_nullary, 1), 53: 54: % when 55: ejabberd_hooks:delete(test_run_hook, ?HOST, hook_mod, fun_nullary, 1), 56: 57: % then 58: [{{test_run_hook,<<"localhost">>}, []}] = get_hooks(). 59: 60: 61: hooks_run_launches_nullary_fun(_) -> 62: given_hooks_started(), 63: given_module(hook_mod, fun_nullary, fun(_) -> #{result => success0} end), 64: given_hook_added(test_run_hook, hook_mod, fun_nullary, 1), 65: 66: %% when 67: run_fold(test_run_hook, ?HOST, ok, []), 68: 69: %% then 70: H = meck:history(hook_mod), 71: [{_,{hook_mod,fun_nullary,[ok]}, #{result := success0}}] = H. 72: 73: hooks_run_launches_unary_fun(_) -> 74: given_hooks_started(), 75: given_module(hook_mod, fun_onearg, fun(Acc, Val) -> Val end), 76: given_hook_added(test_run_hook, hook_mod, fun_onearg, 1), 77: 78: %% when 79: run_fold(test_run_hook, ?HOST, ok, [oneval]), 80: 81: %% then 82: [{_,{hook_mod,fun_onearg,[ok, oneval]}, oneval}] = meck:history(hook_mod). 83: 84: hooks_run_ignores_different_arity_funs(_) -> 85: given_hooks_started(), 86: given_module(hook_mod, fun_onearg, fun(ok, unused) -> never_return end), 87: given_fun(hook_mod, fun_twoarg, fun(ok, one, two)-> success2 end), 88: 89: given_hook_added(test_run_hook, hook_mod, fun_onearg, 1), 90: given_hook_added(test_run_hook, hook_mod, fun_twoarg, 1), 91: 92: %% when 93: run_fold(test_run_hook, ?HOST, ok, [one, two]), 94: 95: %% then 96: [{_,{hook_mod, fun_twoarg, [ok, one, two]}, success2}] = meck:history(hook_mod). 97: 98: hooks_run_stops_when_fun_returns_stop(_) -> 99: given_hooks_started(), 100: given_module(hook_mod, a_fun, const(stop)), 101: given_fun(hook_mod, another_fun, const(success)), 102: 103: given_hook_added(test_run_hook, hook_mod, a_fun, 1), 104: given_hook_added(test_run_hook, hook_mod, another_fun, 2), 105: 106: %% when 107: run_fold(test_run_hook, ?HOST, ok, []), 108: 109: %% then 110: [{_,{hook_mod,a_fun,[ok]}, stop}] = meck:history(hook_mod). 111: 112: 113: hooks_run_fold_folds_with_unary_fun(_) -> 114: given_hooks_started(), 115: given_module(hook_mod, unary_folder, fun(initial) -> done end), 116: given_hook_added(test_fold_hook, hook_mod, unary_folder, 1), 117: 118: %% when 119: run_fold(test_fold_hook, ?HOST, initial, []), 120: 121: %% then 122: [{_,{hook_mod,unary_folder,[initial]}, done}] = meck:history(hook_mod). 123: 124: hooks_run_fold_folds_with_binary_fun(_) -> 125: given_hooks_started(), 126: given_module(hook_mod, binary_folder, fun(initial, arg1) -> done end), 127: given_hook_added(test_fold_hook, hook_mod, binary_folder, 1), 128: 129: %% when 130: run_fold(test_fold_hook, ?HOST, initial, [arg1]), 131: 132: %% then 133: [{_,{hook_mod,binary_folder,[initial, arg1]}, done}] = meck:history(hook_mod). 134: 135: hooks_run_fold_passes_acc_along(_) -> 136: given_hooks_started(), 137: given_module(hook_mod1, first_folder, fun(N, Const) -> N+Const end), 138: given_module(hook_mod2, second_folder, fun(N, Const) -> N-Const*2 end), 139: 140: given_hook_added(test_fold_hook, hook_mod1, first_folder, 1), 141: given_hook_added(test_fold_hook, hook_mod2, second_folder, 2), 142: 143: %% when 144: R = run_fold(test_fold_hook, ?HOST, 0, [10]), 145: 146: %% then 147: -10 = R. 148: 149: hooks_run_fold_stops_when_fun_returns_stop(_) -> 150: given_hooks_started(), 151: given_module(hook_mod1, stopper, const(stop)), 152: given_module(hook_mod2, folder, const(continue)), 153: 154: given_hook_added(test_fold_hook, hook_mod1, stopper, 1), 155: given_hook_added(test_fold_hook, hook_mod2, folder, 2), 156: 157: %% when 158: R = run_fold(test_fold_hook, ?HOST, continue, []), 159: 160: %% then 161: [{_,{hook_mod1,stopper,[continue]}, stop}] = meck:history(hook_mod1), 162: [] = meck:history(hook_mod2), 163: stopped = R. 164: 165: 166: hooks_run_fold_preserves_order(_) -> 167: given_hooks_started(), 168: given_module(hook_mod1, first_folder, const(1)), 169: given_module(hook_mod2, second_folder, const(2)), 170: 171: given_hook_added(test_fold_hook, hook_mod1, first_folder, 1), 172: given_hook_added(test_fold_hook, hook_mod2, second_folder, 2), 173: 174: %% when 175: R = run_fold(test_fold_hook, ?HOST, 0, []), 176: 177: %% then 178: 2 = R. 179: 180: 181: error_in_run_fold_is_ignored(_) -> 182: given_hooks_started(), 183: 184: given_module(failing_mod, broken, fun(_) -> error(broken) end), 185: given_hook_added(test_fold_hook, failing_mod, broken, 1), 186: 187: given_module(working_mod, good, const(i_was_run)), 188: given_hook_added(test_fold_hook, working_mod, good, 2), 189: 190: %% when 191: R = run_fold(test_fold_hook, ?HOST, initial, []), 192: 193: %% then 194: i_was_run = R, 195: [{_Pid, {failing_mod,broken,[initial]}, error,broken, _Stacktrace}] = 196: meck:history(failing_mod). 197: 198: 199: throw_in_run_fold_is_ignored(_) -> 200: given_hooks_started(), 201: 202: given_module(throwing_mod, throwing_fun, fun(_) -> throw(ball) end), 203: given_hook_added(test_fold_hook, throwing_mod, throwing_fun, 1), 204: 205: given_module(working_mod, good, fun(X) -> X end), 206: given_hook_added(test_fold_hook, working_mod, good, 2), 207: 208: %% when 209: R = run_fold(test_fold_hook, ?HOST, initial, []), 210: 211: %% then 212: initial = R, 213: [{_Pid, {throwing_mod,throwing_fun,[initial]}, throw, ball, _ST}] = 214: meck:history(throwing_mod). 215: 216: 217: exit_in_run_fold_is_ignored(_) -> 218: given_hooks_started(), 219: 220: given_module(exiting_mod, exiting_fun, 221: fun(_) -> meck:exception(exit,oops) end), 222: given_hook_added(test_fold_hook, exiting_mod, exiting_fun, 1), 223: 224: given_module(working_mod, good, fun(X) -> X end), 225: given_hook_added(test_fold_hook, working_mod, good, 2), 226: 227: %% when 228: R = run_fold(test_fold_hook, ?HOST, initial, []), 229: 230: %% then 231: initial = R, 232: [{_Pid, {exiting_mod,exiting_fun,[initial]}, exit, oops, _ST}] = 233: meck:history(exiting_mod). 234: 235: 236: 237: 238: %% Givens 239: const(N) -> fun(_) -> N end. 240: 241: given_hooks_started() -> 242: gen_hook:start_link(). 243: 244: given_hook_added(HookName, ModName, FunName, Prio) -> 245: ejabberd_hooks:add(HookName, ?HOST, ModName, FunName, Prio). 246: 247: given_module(ModName, FunName, Fun) -> 248: catch meck:unload(ModName), 249: meck:new(ModName, [non_strict]), 250: meck:expect(ModName, FunName, Fun). 251: 252: given_fun(ModName, FunName, Fun) -> 253: meck:expect(ModName, FunName, Fun). 254: 255: run_fold(HookName, HostType, Acc, Args) -> 256: Params = ejabberd_hooks:add_args(#{}, Args), 257: {_, RetValue} = gen_hook:run_fold(HookName, HostType, Acc, Params), 258: RetValue. 259: 260: get_hooks() -> 261: ets:tab2list(gen_hook).