1: -module(mongoose_instrument_SUITE). 2: -compile([export_all, nowarn_export_all]). 3: 4: -include_lib("eunit/include/eunit.hrl"). 5: -include_lib("common_test/include/ct.hrl"). 6: 7: -define(HANDLER, mongoose_instrument_test_handler). 8: -define(INACTIVE_HANDLER, mongoose_instrument_inactive_handler). 9: -define(ADDED_HANDLER, mongoose_instrument_added_handler). 10: -define(LABELS, #{key => value}). 11: -define(CFG, #{metrics => #{time => histogram}}). 12: -define(MEASUREMENTS, #{count => 1}). 13: 14: %% Setup and teardown 15: 16: all() -> 17: [{group, persistent}, 18: {group, not_persistent} 19: ]. 20: 21: groups() -> 22: [{persistent, [parallel], api_test_cases()}, 23: {not_persistent, [parallel], api_test_cases()}]. 24: 25: api_test_cases() -> 26: [set_up_and_execute, 27: set_up_multiple_and_execute_one, 28: set_up_fails_when_already_registered, 29: set_up_fails_for_inconsistent_labels, 30: set_up_and_tear_down, 31: set_up_and_tear_down_multiple, 32: execute_fails_when_not_set_up, 33: set_up_and_span, 34: set_up_and_span_with_arg, 35: set_up_and_span_with_error, 36: span_fails_when_not_set_up, 37: unexpected_events, 38: add_and_remove_handler, 39: cannot_add_existing_handler, 40: cannot_remove_non_existing_handler]. 41: 42: init_per_suite(Config) -> 43: mongoose_config:set_opts(opts()), 44: mock_handler(?HANDLER, true), 45: mock_handler(?INACTIVE_HANDLER, false), 46: mock_handler(?ADDED_HANDLER, true), 47: Config. 48: 49: mock_handler(Module, SetUpResult) -> 50: meck:new(Module, [non_strict, no_link]), 51: meck:expect(Module, set_up, fun(_Event, #{}, #{}) -> SetUpResult end), 52: meck:expect(Module, handle_event, fun(_Event, #{}, #{}, #{}) -> ok end). 53: 54: end_per_suite(_Config) -> 55: meck:unload(?HANDLER), 56: meck:unload(?INACTIVE_HANDLER), 57: meck:unload(?ADDED_HANDLER), 58: mongoose_config:erase_opts(). 59: 60: init_per_group(Group, Config) -> 61: meck:reset(?HANDLER), 62: meck:reset(?INACTIVE_HANDLER), 63: meck:reset(?ADDED_HANDLER), 64: Config1 = async_helper:start(Config, mongoose_instrument, start_link, []), 65: set_up_persistence(Group), 66: Config1. 67: 68: set_up_persistence(persistent) -> 69: mongoose_instrument:persist(), 70: ?assertEqual(#{}, persistent_term:get(mongoose_instrument)); 71: set_up_persistence(not_persistent) -> 72: ?assertError(badarg, persistent_term:get(mongoose_instrument)). 73: 74: end_per_group(_Group, Config) -> 75: async_helper:stop_all(Config), 76: mongoose_helper:wait_until(fun() -> whereis(mongoose_instrument) end, undefined), 77: ?assertError(badarg, persistent_term:get(mongoose_instrument)). 78: 79: init_per_testcase(Case, Config) -> 80: [{event, event_name(Case)} | Config]. 81: 82: end_per_testcase(_Case, _Config) -> 83: ok. 84: 85: opts() -> 86: #{instrumentation => #{test_handler => #{}, inactive_handler => #{}}}. 87: 88: event_name(Case) -> 89: list_to_atom(atom_to_list(Case) ++ "_event"). 90: 91: %% Test cases 92: 93: set_up_and_execute(Config) -> 94: Event = ?config(event, Config), 95: ok = mongoose_instrument:set_up(Event, ?LABELS, ?CFG), 96: ?assertEqual([{[Event, ?LABELS, ?CFG], true}], history(?HANDLER, set_up, Event)), 97: ?assertEqual([{[Event, ?LABELS, ?CFG], false}], history(?INACTIVE_HANDLER, set_up, Event)), 98: ok = mongoose_instrument:execute(Event, ?LABELS, ?MEASUREMENTS), 99: ?assertEqual([{[Event, ?LABELS, ?CFG, ?MEASUREMENTS], ok}], 100: history(?HANDLER, handle_event, Event)), 101: ?assertEqual([], history(?INACTIVE_HANDLER, handle_event, Event)). 102: 103: set_up_multiple_and_execute_one(Config) -> 104: Event = ?config(event, Config), 105: Specs = [{Event, Labels1 = #{key => value1}, ?CFG}, 106: {Event, Labels2 = #{key => value2}, ?CFG}], 107: ok = mongoose_instrument:set_up(Specs), 108: ?assertEqual([{[Event, Labels1, ?CFG], true}, {[Event, Labels2, ?CFG], true}], 109: history(?HANDLER, set_up, Event)), 110: ok = mongoose_instrument:execute(Event, Labels1, ?MEASUREMENTS), 111: ?assertEqual([{[Event, Labels1, ?CFG, ?MEASUREMENTS], ok}], 112: history(?HANDLER, handle_event, Event)). 113: 114: set_up_fails_when_already_registered(Config) -> 115: Event = ?config(event, Config), 116: ok = mongoose_instrument:set_up(Event, ?LABELS, ?CFG), 117: ?assertError(#{what := event_already_registered}, 118: mongoose_instrument:set_up(Event, ?LABELS, ?CFG)), 119: ?assertError(#{what := event_already_registered}, 120: mongoose_instrument:set_up(Event, ?LABELS, #{})). 121: 122: set_up_fails_for_inconsistent_labels(Config) -> 123: Event = ?config(event, Config), 124: Labels = ?LABELS, 125: ok = mongoose_instrument:set_up(Event, Labels, ?CFG), 126: ?assertError(#{what := inconsistent_labels}, 127: mongoose_instrument:set_up(Event, #{}, ?CFG)), 128: ?assertError(#{what := inconsistent_labels}, 129: mongoose_instrument:set_up(Event, Labels#{additional_key => value}, ?CFG)), 130: ?assertError(#{what := inconsistent_labels}, 131: mongoose_instrument:set_up(Event, #{different_key => value}, ?CFG)). 132: 133: set_up_and_tear_down(Config) -> 134: Event = ?config(event, Config), 135: ok = mongoose_instrument:set_up(Event, ?LABELS, ?CFG), 136: ok = mongoose_instrument:tear_down(Event, ?LABELS), 137: ?assertError(#{what := event_not_registered}, 138: mongoose_instrument:execute(Event, ?LABELS, ?MEASUREMENTS)), 139: [] = history(?HANDLER, handle_event, Event), 140: ok = mongoose_instrument:tear_down(Event, ?LABELS). % idempotent 141: 142: set_up_and_tear_down_multiple(Config) -> 143: Event = ?config(event, Config), 144: Specs = [{Event, Labels1 = #{key => value1}, ?CFG}, 145: {Event, Labels2 = #{key => value2}, ?CFG}], 146: ok = mongoose_instrument:set_up(Specs), 147: ok = mongoose_instrument:tear_down(Specs), 148: ?assertError(#{what := event_not_registered}, 149: mongoose_instrument:execute(Event, Labels1, ?MEASUREMENTS)), 150: ?assertError(#{what := event_not_registered}, 151: mongoose_instrument:execute(Event, Labels2, ?MEASUREMENTS)), 152: [] = history(?HANDLER, handle_event, Event). 153: 154: execute_fails_when_not_set_up(Config) -> 155: Event = ?config(event, Config), 156: ?assertError(#{what := event_not_registered}, 157: mongoose_instrument:execute(Event, ?LABELS, ?MEASUREMENTS)), 158: [] = history(?HANDLER, handle_event, Event). 159: 160: set_up_and_span(Config) -> 161: {Event, Labels, InstrConfig} = {?config(event, Config), ?LABELS, ?CFG}, 162: ok = mongoose_instrument:set_up(Event, Labels, InstrConfig), 163: ok = mongoose_instrument:span(Event, Labels, fun test_op/0, fun measure_test_op/2), 164: [{[Event, Labels, InstrConfig, #{time := Time, result := ok}], ok}] = 165: history(?HANDLER, handle_event, Event), 166: ?assert(Time >= 1000). 167: 168: set_up_and_span_with_arg(Config) -> 169: {Event, Labels, InstrConfig} = {?config(event, Config), ?LABELS, ?CFG}, 170: ok = mongoose_instrument:set_up(Event, Labels, InstrConfig), 171: ok = mongoose_instrument:span(Event, Labels, fun test_op/1, [2], fun measure_test_op/2), 172: [{[Event, Labels, InstrConfig, #{time := Time, result := ok}], ok}] = 173: history(?HANDLER, handle_event, Event), 174: ?assert(Time >= 2000). 175: 176: set_up_and_span_with_error(Config) -> 177: Event = ?config(event, Config), 178: ok = mongoose_instrument:set_up(Event, ?LABELS, ?CFG), 179: ?assertError(simulated_error, 180: mongoose_instrument:span(Event, ?LABELS, fun crashing_op/0, fun measure_test_op/2)), 181: [] = history(?HANDLER, handle_event, Event). 182: 183: span_fails_when_not_set_up(Config) -> 184: Event = ?config(event, Config), 185: Labels = #{key => value}, 186: ?assertError(#{what := event_not_registered}, 187: mongoose_instrument:span(Event, Labels, fun test_op/0, fun measure_test_op/2)), 188: %% Also checks that the function is not executed - otherwise 'simulated_error' would be raised 189: ?assertError(#{what := event_not_registered}, 190: mongoose_instrument:span(Event, Labels, fun crashing_op/0, fun measure_test_op/2)), 191: [] = history(?HANDLER, handle_event, Event). 192: 193: unexpected_events(_Config) -> 194: Pid = whereis(mongoose_instrument), 195: {error, #{what := unexpected_call}} = gen_server:call(mongoose_instrument, bad_call), 196: gen_server:cast(mongoose_instrument, bad_cast), 197: mongoose_instrument ! bad_info, 198: ?assertEqual(Pid, whereis(mongoose_instrument)). %% It should be still working 199: 200: add_and_remove_handler(Config) -> 201: Event = ?config(event, Config), 202: ok = mongoose_instrument:set_up(Event, Labels1 = #{key => value1}, ?CFG), 203: 204: %% Add handler 205: ok = mongoose_instrument:add_handler(added_handler, #{cfg_key => cfg_val}), 206: ?assertEqual(#{cfg_key => cfg_val}, mongoose_config:get_opt([instrumentation, added_handler])), 207: ?assertEqual([{[Event, Labels1, ?CFG], true}], history(?ADDED_HANDLER, set_up, Event)), 208: %% Make sure it's still possible to set up new events 209: ok = mongoose_instrument:set_up(Event, Labels2 = #{key => value2}, ?CFG), 210: 211: ok = mongoose_instrument:execute(Event, Labels1, ?MEASUREMENTS), 212: ok = mongoose_instrument:execute(Event, Labels2, ?MEASUREMENTS), 213: History = history(?ADDED_HANDLER, handle_event, Event), 214: ?assertEqual([{[Event, Labels1, ?CFG, ?MEASUREMENTS], ok}, 215: {[Event, Labels2, ?CFG, ?MEASUREMENTS], ok}], History), 216: 217: %% Remove handler 218: ok = mongoose_instrument:remove_handler(added_handler), 219: ?assertEqual({error, not_found}, mongoose_config:lookup_opt([instrumentation, added_handler])), 220: ok = mongoose_instrument:execute(Event, Labels1, ?MEASUREMENTS), 221: ok = mongoose_instrument:execute(Event, Labels2, ?MEASUREMENTS), 222: ?assertEqual(History, history(?ADDED_HANDLER, handle_event, Event)). % no new events 223: 224: cannot_add_existing_handler(_Config) -> 225: ?assertError(#{what := handler_already_configured}, 226: mongoose_instrument:add_handler(test_handler, #{})). 227: 228: cannot_remove_non_existing_handler(_Config) -> 229: ?assertError(#{what := handler_not_configured}, 230: mongoose_instrument:remove_handler(unknown_handler)). 231: 232: %% Helpers 233: 234: history(HandlerMod, WantedF, WantedEvent) -> 235: [{Args, Result} || {_Pid, {_M, CalledF, [Event|_] = Args}, Result} <- meck:history(HandlerMod), 236: WantedF =:= CalledF andalso WantedEvent =:= Event]. 237: 238: test_op(Delay) -> 239: timer:sleep(Delay). 240: 241: test_op() -> 242: test_op(1). 243: 244: crashing_op() -> 245: error(simulated_error). 246: 247: measure_test_op(Time, Result) -> 248: #{time => Time, result => Result}.