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