Add test suite for couch_log COUCHDB-3067
Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch-log/repo Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch-log/commit/16a46572 Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch-log/tree/16a46572 Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch-log/diff/16a46572 Branch: refs/heads/master Commit: 16a46572b92bf0b53b3aa94bc209cae0ba2257c1 Parents: c572aae Author: Paul J. Davis <[email protected]> Authored: Wed Jul 20 12:53:50 2016 -0500 Committer: Paul J. Davis <[email protected]> Committed: Fri Jul 22 04:39:08 2016 -0500 ---------------------------------------------------------------------- include/couch_log.hrl | 3 + rebar.config | 2 + test/couch_log_config_listener_test.erl | 56 ++ test/couch_log_config_test.erl | 110 ++++ test/couch_log_error_logger_h_test.erl | 45 ++ test/couch_log_formatter_test.erl | 768 +++++++++++++++++++++++++++ test/couch_log_monitor_test.erl | 67 +++ test/couch_log_server_test.erl | 118 ++++ test/couch_log_test.erl | 85 +++ test/couch_log_test_util.erl | 153 ++++++ test/couch_log_util_test.erl | 55 ++ test/couch_log_writer_ets.erl | 49 ++ test/couch_log_writer_file_test.erl | 161 ++++++ test/couch_log_writer_stderr_test.erl | 58 ++ test/couch_log_writer_syslog_test.erl | 122 +++++ test/couch_log_writer_test.erl | 54 ++ 16 files changed, 1906 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/couchdb-couch-log/blob/16a46572/include/couch_log.hrl ---------------------------------------------------------------------- diff --git a/include/couch_log.hrl b/include/couch_log.hrl index a472c0c..fa544a8 100644 --- a/include/couch_log.hrl +++ b/include/couch_log.hrl @@ -17,3 +17,6 @@ msg_id, time_stamp }). + + +-define(COUCH_LOG_TEST_TABLE, couch_log_test_table). http://git-wip-us.apache.org/repos/asf/couchdb-couch-log/blob/16a46572/rebar.config ---------------------------------------------------------------------- diff --git a/rebar.config b/rebar.config new file mode 100644 index 0000000..e0d1844 --- /dev/null +++ b/rebar.config @@ -0,0 +1,2 @@ +{cover_enabled, true}. +{cover_print_enabled, true}. http://git-wip-us.apache.org/repos/asf/couchdb-couch-log/blob/16a46572/test/couch_log_config_listener_test.erl ---------------------------------------------------------------------- diff --git a/test/couch_log_config_listener_test.erl b/test/couch_log_config_listener_test.erl new file mode 100644 index 0000000..9a8e16d --- /dev/null +++ b/test/couch_log_config_listener_test.erl @@ -0,0 +1,56 @@ +% Licensed under the Apache License, Version 2.0 (the "License"); you may not +% use this file except in compliance with the License. You may obtain a copy of +% the License at +% +% http://www.apache.org/licenses/LICENSE-2.0 +% +% Unless required by applicable law or agreed to in writing, software +% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +% License for the specific language governing permissions and limitations under +% the License. + +-module(couch_log_config_listener_test). + + +-include_lib("couch_log/include/couch_log.hrl"). +-include_lib("eunit/include/eunit.hrl"). + + +-define(HANDLER, {config_listener, couch_log_config_listener}). + + +couch_log_config_test_() -> + {setup, + fun couch_log_test_util:start/0, + fun couch_log_test_util:stop/1, + [ + fun check_restart_listener/0, + fun check_ignore_non_log/0 + ] + }. + + +check_restart_listener() -> + Handlers1 = gen_event:which_handlers(config_event), + ?assert(lists:member(?HANDLER, Handlers1)), + + gen_event:delete_handler(config_event, ?HANDLER, testing), + + Handlers2 = gen_event:which_handlers(config_event), + ?assert(not lists:member(?HANDLER, Handlers2)), + + timer:sleep(1000), + + Handlers3 = gen_event:which_handlers(config_event), + ?assert(lists:member(?HANDLER, Handlers3)). + + +check_ignore_non_log() -> + Run = fun() -> + couch_log_test_util:with_config_listener(fun() -> + config:set("foo", "bar", "baz"), + couch_log_test_util:wait_for_config() + end) + end, + ?assertError(config_change_timeout, Run()). http://git-wip-us.apache.org/repos/asf/couchdb-couch-log/blob/16a46572/test/couch_log_config_test.erl ---------------------------------------------------------------------- diff --git a/test/couch_log_config_test.erl b/test/couch_log_config_test.erl new file mode 100644 index 0000000..c4677f3 --- /dev/null +++ b/test/couch_log_config_test.erl @@ -0,0 +1,110 @@ +% Licensed under the Apache License, Version 2.0 (the "License"); you may not +% use this file except in compliance with the License. You may obtain a copy of +% the License at +% +% http://www.apache.org/licenses/LICENSE-2.0 +% +% Unless required by applicable law or agreed to in writing, software +% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +% License for the specific language governing permissions and limitations under +% the License. + +-module(couch_log_config_test). + + +-include_lib("couch_log/include/couch_log.hrl"). +-include_lib("eunit/include/eunit.hrl"). + + +couch_log_config_test_() -> + {setup, + fun couch_log_test_util:start/0, + fun couch_log_test_util:stop/1, + [ + fun check_level/0, + fun check_max_message_size/0, + fun check_bad_level/0, + fun check_bad_max_message_size/0 + ] + }. + + +check_level() -> + % Default level is info + ?assertEqual(info, couch_log_config:get(level)), + ?assertEqual(2, couch_log_config:get(level_int)), + + couch_log_test_util:with_config_listener(fun() -> + config:set("log", "level", "emerg"), + couch_log_test_util:wait_for_config(), + ?assertEqual(emergency, couch_log_config:get(level)), + ?assertEqual(8, couch_log_config:get(level_int)), + + config:set("log", "level", "debug"), + couch_log_test_util:wait_for_config(), + ?assertEqual(debug, couch_log_config:get(level)), + ?assertEqual(1, couch_log_config:get(level_int)), + + config:delete("log", "level"), + couch_log_test_util:wait_for_config(), + ?assertEqual(info, couch_log_config:get(level)), + ?assertEqual(2, couch_log_config:get(level_int)) + end). + + +check_max_message_size() -> + % Default is 16000 + ?assertEqual(16000, couch_log_config:get(max_message_size)), + + couch_log_test_util:with_config_listener(fun() -> + config:set("log", "max_message_size", "1024"), + couch_log_test_util:wait_for_config(), + ?assertEqual(1024, couch_log_config:get(max_message_size)), + + config:delete("log", "max_message_size"), + couch_log_test_util:wait_for_config(), + ?assertEqual(16000, couch_log_config:get(max_message_size)) + end). + + +check_bad_level() -> + % Default level is info + ?assertEqual(info, couch_log_config:get(level)), + ?assertEqual(2, couch_log_config:get(level_int)), + + couch_log_test_util:with_config_listener(fun() -> + config:set("log", "level", "debug"), + couch_log_test_util:wait_for_config(), + ?assertEqual(debug, couch_log_config:get(level)), + ?assertEqual(1, couch_log_config:get(level_int)), + + config:set("log", "level", "this is not a valid level name"), + couch_log_test_util:wait_for_config(), + ?assertEqual(info, couch_log_config:get(level)), + ?assertEqual(2, couch_log_config:get(level_int)), + + config:delete("log", "level"), + couch_log_test_util:wait_for_config(), + ?assertEqual(info, couch_log_config:get(level)), + ?assertEqual(2, couch_log_config:get(level_int)) + end). + + +check_bad_max_message_size() -> + % Default level is 16000 + ?assertEqual(16000, couch_log_config:get(max_message_size)), + + couch_log_test_util:with_config_listener(fun() -> + config:set("log", "max_message_size", "1024"), + couch_log_test_util:wait_for_config(), + ?assertEqual(1024, couch_log_config:get(max_message_size)), + + config:set("log", "max_message_size", "this is not a valid size"), + couch_log_test_util:wait_for_config(), + ?assertEqual(16000, couch_log_config:get(max_message_size)), + + config:delete("log", "max_message_size"), + couch_log_test_util:wait_for_config(), + ?assertEqual(16000, couch_log_config:get(max_message_size)) + end). http://git-wip-us.apache.org/repos/asf/couchdb-couch-log/blob/16a46572/test/couch_log_error_logger_h_test.erl ---------------------------------------------------------------------- diff --git a/test/couch_log_error_logger_h_test.erl b/test/couch_log_error_logger_h_test.erl new file mode 100644 index 0000000..b78598f --- /dev/null +++ b/test/couch_log_error_logger_h_test.erl @@ -0,0 +1,45 @@ +% Licensed under the Apache License, Version 2.0 (the "License"); you may not +% use this file except in compliance with the License. You may obtain a copy of +% the License at +% +% http://www.apache.org/licenses/LICENSE-2.0 +% +% Unless required by applicable law or agreed to in writing, software +% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +% License for the specific language governing permissions and limitations under +% the License. + +-module(couch_log_error_logger_h_test). + + +-include_lib("eunit/include/eunit.hrl"). + + +-define(HANDLER, couch_log_error_logger_h). + + +couch_log_error_logger_h_test_() -> + {setup, + fun couch_log_test_util:start/0, + fun couch_log_test_util:stop/1, + [ + fun handler_ignores_unknown_messages/0, + fun coverage_test/0 + ] + }. + + +handler_ignores_unknown_messages() -> + Handlers1 = gen_event:which_handlers(error_logger), + ?assert(lists:member(?HANDLER, Handlers1)), + ?assertEqual(ignored, gen_event:call(error_logger, ?HANDLER, foo)), + + error_logger ! this_is_a_message, + Handlers2 = gen_event:which_handlers(error_logger), + ?assert(lists:member(?HANDLER, Handlers2)). + + +coverage_test() -> + Resp = couch_log_error_logger_h:code_change(foo, bazinga, baz), + ?assertEqual({ok, bazinga}, Resp). http://git-wip-us.apache.org/repos/asf/couchdb-couch-log/blob/16a46572/test/couch_log_formatter_test.erl ---------------------------------------------------------------------- diff --git a/test/couch_log_formatter_test.erl b/test/couch_log_formatter_test.erl new file mode 100644 index 0000000..1e8457b --- /dev/null +++ b/test/couch_log_formatter_test.erl @@ -0,0 +1,768 @@ +% Licensed under the Apache License, Version 2.0 (the "License"); you may not +% use this file except in compliance with the License. You may obtain a copy of +% the License at +% +% http://www.apache.org/licenses/LICENSE-2.0 +% +% Unless required by applicable law or agreed to in writing, software +% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +% License for the specific language governing permissions and limitations under +% the License. + +-module(couch_log_formatter_test). + + +-include("couch_log.hrl"). +-include_lib("eunit/include/eunit.hrl"). + + +truncate_fmt_test() -> + Msg = [0 || _ <- lists:seq(1, 1048576)], + Entry = couch_log_formatter:format(info, self(), "~w", [Msg]), + ?assert(length(Entry#log_entry.msg) =< 16000). + + +truncate_test() -> + Msg = [0 || _ <- lists:seq(1, 1048576)], + Entry = couch_log_formatter:format(info, self(), Msg), + ?assert(length(Entry#log_entry.msg) =< 16000). + + +gen_server_error_test() -> + Pid = self(), + Event = { + error, + erlang:group_leader(), + { + Pid, + "** Generic server and some stuff", + [a_gen_server, {foo, bar}, server_state, some_reason] + } + }, + ?assertMatch( + #log_entry{ + level = error, + pid = Pid + }, + do_format(Event) + ), + do_matches(do_format(Event), [ + "gen_server a_gen_server terminated", + "with reason: some_reason", + "last msg: {foo,bar}", + "state: server_state" + ]). + + +gen_fsm_error_test() -> + Pid = self(), + Event = { + error, + erlang:group_leader(), + { + Pid, + "** State machine did a thing", + [a_gen_fsm, {ohai,there}, state_name, curr_state, barf] + } + }, + ?assertMatch( + #log_entry{ + level = error, + pid = Pid + }, + do_format(Event) + ), + do_matches(do_format(Event), [ + "gen_fsm a_gen_fsm in state state_name", + "with reason: barf", + "last msg: {ohai,there}", + "state: curr_state" + ]). + + +gen_event_error_test() -> + Pid = self(), + Event = { + error, + erlang:group_leader(), + { + Pid, + "** gen_event handler did a thing", + [ + handler_id, + a_gen_event, + {ohai,there}, + curr_state, + barf + ] + } + }, + ?assertMatch( + #log_entry{ + level = error, + pid = Pid + }, + do_format(Event) + ), + do_matches(do_format(Event), [ + "gen_event handler_id installed in a_gen_event", + "reason: barf", + "last msg: {ohai,there}", + "state: curr_state" + ]). + + +normal_error_test() -> + Pid = self(), + Event = { + error, + erlang:group_leader(), + { + Pid, + "format thing: ~w ~w", + [ + first_arg, + second_arg + ] + } + }, + ?assertMatch( + #log_entry{ + level = error, + pid = Pid, + msg = "format thing: first_arg second_arg" + }, + do_format(Event) + ). + + +error_report_std_error_test() -> + Pid = self(), + Event = { + error_report, + erlang:group_leader(), + { + Pid, + std_error, + [foo, {bar, baz}] + } + }, + ?assertMatch( + #log_entry{ + level = error, + pid = Pid, + msg = "foo, bar: baz" + }, + do_format(Event) + ). + + +supervisor_report_test() -> + Pid = self(), + % A standard supervisor report + Event1 = { + error_report, + erlang:group_leader(), + { + Pid, + supervisor_report, + [ + {supervisor, sup_name}, + {offender, [ + {id, sup_child}, + {pid, list_to_pid("<0.1.0>")}, + {mfargs, {some_mod, some_fun, 3}} + ]}, + {reason, a_reason}, + {errorContext, some_context} + ] + } + }, + ?assertMatch( + #log_entry{ + level = error, + pid = Pid + }, + do_format(Event1) + ), + do_matches(do_format(Event1), [ + "Supervisor sup_name", + "had child sup_child started with some_mod:some_fun/3 at <0.1.0> exit", + "with reason a_reason", + "in context some_context" + ]), + % Slightly older using name instead of id + % in the offender blob. + Event2 = { + error_report, + erlang:group_leader(), + { + Pid, + supervisor_report, + [ + {supervisor, sup_name}, + {offender, [ + {name, sup_child}, + {pid, list_to_pid("<0.1.0>")}, + {mfargs, {some_mod, some_fun, 3}} + ]}, + {reason, a_reason}, + {errorContext, some_context} + ] + } + }, + ?assertMatch( + #log_entry{ + level = error, + pid = Pid + }, + do_format(Event2) + ), + do_matches(do_format(Event2), [ + "Supervisor sup_name", + "had child sup_child started with some_mod:some_fun/3 at <0.1.0> exit", + "with reason a_reason", + "in context some_context" + ]), + % A supervisor_bridge + Event3 = { + error_report, + erlang:group_leader(), + { + Pid, + supervisor_report, + [ + {supervisor, sup_name}, + {offender, [ + {mod, bridge_mod}, + {pid, list_to_pid("<0.1.0>")} + ]}, + {reason, a_reason}, + {errorContext, some_context} + ] + } + }, + ?assertMatch( + #log_entry{ + level = error, + pid = Pid + }, + do_format(Event3) + ), + do_matches(do_format(Event3), [ + "Supervisor sup_name", + "had child at module bridge_mod at <0.1.0> exit", + "with reason a_reason", + "in context some_context" + ]), + % Any other supervisor report + Event4 = { + error_report, + erlang:group_leader(), + { + Pid, + supervisor_report, + [foo, {a, thing}, bang] + } + }, + ?assertMatch( + #log_entry{ + level = error, + pid = Pid, + msg = "SUPERVISOR REPORT foo, a: thing, bang" + }, + do_format(Event4) + ). + + +crash_report_test() -> + Pid = self(), + % A standard crash report + Event1 = { + error_report, + erlang:group_leader(), + { + Pid, + crash_report, + [ + [ + {pid, list_to_pid("<0.2.0>")}, + {error_info, { + exit, + undef, + [{mod_name, fun_name, [a, b]}] + }} + ], + [list_to_pid("<0.3.0>"), list_to_pid("<0.4.0>")] + ] + } + }, + ?assertMatch( + #log_entry{ + level = error, + pid = Pid + }, + do_format(Event1) + ), + do_matches(do_format(Event1), [ + "Process <0.2.0>", + "with 2 neighbors", + "exited", + "reason: call to undefined function mod_name:fun_name\\(a, b\\)" + ]), + % A registered process crash report + Event2 = { + error_report, + erlang:group_leader(), + { + Pid, + crash_report, + [ + [ + {pid, list_to_pid("<0.2.0>")}, + {registered_name, couch_log_server}, + {error_info, { + exit, + undef, + [{mod_name, fun_name, [a, b]}] + }} + ], + [list_to_pid("<0.3.0>"), list_to_pid("<0.4.0>")] + ] + } + }, + do_matches(do_format(Event2), [ + "Process couch_log_server \\(<0.2.0>\\)" + ]), + % A non-exit crash report + Event3 = { + error_report, + erlang:group_leader(), + { + Pid, + crash_report, + [ + [ + {pid, list_to_pid("<0.2.0>")}, + {registered_name, couch_log_server}, + {error_info, { + killed, + undef, + [{mod_name, fun_name, [a, b]}] + }} + ], + [list_to_pid("<0.3.0>"), list_to_pid("<0.4.0>")] + ] + } + }, + do_matches(do_format(Event3), [ + "crashed" + ]), + % A extra report info + Event4 = { + error_report, + erlang:group_leader(), + { + Pid, + crash_report, + [ + [ + {pid, list_to_pid("<0.2.0>")}, + {error_info, { + killed, + undef, + [{mod_name, fun_name, [a, b]}] + }}, + {another, entry}, + yep + ], + [list_to_pid("<0.3.0>"), list_to_pid("<0.4.0>")] + ] + } + }, + do_matches(do_format(Event4), [ + "; another: entry, yep" + ]). + + +warning_report_test() -> + Pid = self(), + % A warning message + Event1 = { + warning_msg, + erlang:group_leader(), + { + Pid, + "a ~s string ~w", + ["format", 7] + } + }, + ?assertMatch( + #log_entry{ + level = warning, + pid = Pid, + msg = "a format string 7" + }, + do_format(Event1) + ), + % A warning report + Event2 = { + warning_report, + erlang:group_leader(), + { + Pid, + std_warning, + [list, 'of', {things, indeed}] + } + }, + ?assertMatch( + #log_entry{ + level = warning, + pid = Pid, + msg = "list, of, things: indeed" + }, + do_format(Event2) + ). + + +info_report_test() -> + Pid = self(), + % An info message + Event1 = { + info_msg, + erlang:group_leader(), + { + Pid, + "an info ~s string ~w", + ["format", 7] + } + }, + ?assertMatch( + #log_entry{ + level = info, + pid = Pid, + msg = "an info format string 7" + }, + do_format(Event1) + ), + % Application exit info + Event2 = { + info_report, + erlang:group_leader(), + { + Pid, + std_info, + [ + {type, no_idea}, + {application, couch_log}, + {exited, red_sox_are_on} + ] + } + }, + ?assertMatch( + #log_entry{ + level = info, + pid = Pid, + msg = "Application couch_log exited with reason: red_sox_are_on" + }, + do_format(Event2) + ), + % Any other std_info message + Event3 = { + info_report, + erlang:group_leader(), + { + Pid, + std_info, + [ + {type, no_idea}, + {application, couch_log} + ] + } + }, + ?assertMatch( + #log_entry{ + level = info, + pid = Pid, + msg = "type: no_idea, application: couch_log" + }, + do_format(Event3) + ), + % Non-list other report + Event4 = { + info_report, + erlang:group_leader(), + { + Pid, + std_info, + dang + } + }, + ?assertMatch( + #log_entry{ + level = info, + pid = Pid, + msg = "dang" + }, + do_format(Event4) + ). + + +progress_report_test() -> + Pid = self(), + % Application started + Event1 = { + info_report, + erlang:group_leader(), + { + Pid, + progress, + [{started_at, 'nonode@nohost'}, {application, app_name}] + } + }, + ?assertMatch( + #log_entry{ + level = info, + pid = Pid, + msg = "Application app_name started on node nonode@nohost" + }, + do_format(Event1) + ), + % Supervisor started child + Event2 = { + info_report, + erlang:group_leader(), + { + Pid, + progress, + [ + {supervisor, sup_dude}, + {started, [ + {mfargs, {mod_name, fun_name, 1}}, + {pid, list_to_pid("<0.5.0>")} + ]} + ] + } + }, + ?assertMatch( + #log_entry{ + level = debug, + pid = Pid, + msg = "Supervisor sup_dude started mod_name:fun_name/1" + " at pid <0.5.0>" + }, + do_format(Event2) + ), + % Other progress report + Event3 = { + info_report, + erlang:group_leader(), + { + Pid, + progress, + [a, {thing, boop}, here] + } + }, + ?assertMatch( + #log_entry{ + level = info, + pid = Pid, + msg = "PROGRESS REPORT a, thing: boop, here" + }, + do_format(Event3) + ). + + +log_unknown_event_test() -> + Pid = self(), + ?assertMatch( + #log_entry{ + level = warning, + pid = Pid, + msg = "Unexpected error_logger event an_unknown_event" + }, + do_format(an_unknown_event) + ). + + +format_reason_test_() -> + Cases = [ + { + {'function not exported', [{a, b, 2}, {c, d, 1}, {e, f, 2}]}, + "call to unexported function a:b/2 at c:d/1 <= e:f/2" + }, + { + {'function not exported', [{a, b, 2, []}, {c, d, 1}, {e, f, 2}]}, + "call to unexported function a:b/2 at c:d/1 <= e:f/2" + }, + { + {undef, [{a, b, 2, []}, {c, d, 1}, {e, f, 2}]}, + "call to undefined function a:b/2 at c:d/1 <= e:f/2" + }, + { + {bad_return, {{a, b, 2}, {'EXIT', killed}}}, + "bad return value {'EXIT',killed} from a:b/2" + }, + { + {bad_return_value, foo}, + "bad return value foo" + }, + { + {{bad_return_value, foo}, {h, i, 0}}, + "bad return value foo at h:i/0" + }, + { + {{badrecord, {foo, 1, 4}}, [{h, i, 0}, {j, k, [a, b]}]}, + "bad record {foo,1,4} at h:i/0 <= j:k/2" + }, + { + {{case_clause, bingo}, [{j, k, 3}, {z, z, 0}]}, + "no case clause matching bingo at j:k/3 <= z:z/0" + }, + { + {function_clause, [{j, k, [a, 2]}, {y, x, 1}]}, + "no function clause matching j:k(a, 2) at y:x/1" + }, + { + {if_clause, [{j, k, [a, 2]}, {y, x, 1}]}, + "no true branch found while evaluating if expression at j:k/2 <= y:x/1" + }, + { + {{try_clause, bango}, [{j, k, [a, 2]}, {y, x, 1}]}, + "no try clause matching bango at j:k/2 <= y:x/1" + }, + { + {badarith, [{j, k, [a, 2]}, {y, x, 1}]}, + "bad arithmetic expression at j:k/2 <= y:x/1" + }, + { + {{badmatch, bongo}, [{j, k, [a, 2]}, {y, x, 1}]}, + "no match of right hand value bongo at j:k/2 <= y:x/1" + }, + { + {emfile, [{j, k, [a, 2]}, {y, x, 1}]}, + "maximum number of file descriptors exhausted, check ulimit -n; j:k/2 <= y:x/1" + }, + { + {system_limit, [{erlang, open_port, []}, {y, x, 1}]}, + "system limit: maximum number of ports exceeded at y:x/1" + }, + { + {system_limit, [{erlang, spawn, []}, {y, x, 1}]}, + "system limit: maximum number of processes exceeded at y:x/1" + }, + { + {system_limit, [{erlang, spawn_opt, []}, {y, x, 1}]}, + "system limit: maximum number of processes exceeded at y:x/1" + }, + { + {system_limit, [{erlang, list_to_atom, ["foo"]}, {y, x, 1}]}, + "system limit: tried to create an atom larger than 255, or maximum atom count exceeded at y:x/1" + }, + { + {system_limit, [{ets, new, []}, {y, x, 1}]}, + "system limit: maximum number of ETS tables exceeded at y:x/1" + }, + { + {system_limit, [{couch_log, totes_logs, []}, {y, x, 1}]}, + "system limit: couch_log:totes_logs() at y:x/1" + }, + { + {badarg, [{j, k, [a, 2]}, {y, x, 1}]}, + "bad argument in call to j:k(a, 2) at y:x/1" + }, + { + {{badarg, [{j, k, [a, 2]}, {y, x, 1}]}, some_ignored_thing}, + "bad argument in call to j:k(a, 2) at y:x/1" + }, + { + {{badarity, {fun erlang:spawn/1, [a, b]}}, [{y, x, 1}]}, + "function called with wrong arity of 2 instead of 1 at y:x/1" + }, + { + {noproc, [{y, x, 1}]}, + "no such process or port in call to y:x/1" + }, + { + {{badfun, 2}, [{y, x, 1}]}, + "bad function 2 called at y:x/1" + }, + { + {a_reason, [{y, x, 1}]}, + "a_reason at y:x/1" + }, + { + {a_reason, [{y, x, 1, [{line, 4}]}]}, + "a_reason at y:x/1(line:4)" + } + ], + [ + {Msg, fun() -> ?assertEqual( + Msg, + lists:flatten(couch_log_formatter:format_reason(Reason)) + ) end} + || {Reason, Msg} <- Cases + ]. + + +coverage_test() -> + % MFA's that aren't + ?assertEqual(["foo"], couch_log_formatter:format_mfa(foo)), + + % Traces with line numbers + Trace = [{x, y, [a], [{line, 4}]}], + ?assertEqual( + "x:y/1(line:4)", + lists:flatten(couch_log_formatter:format_trace(Trace)) + ), + + % Excercising print_silly_list + ?assertMatch( + #log_entry{ + level = error, + msg = "foobar" + }, + do_format({ + error_report, + erlang:group_leader(), + {self(), std_error, "foobar"} + }) + ), + + % Excercising print_silly_list + ?assertMatch( + #log_entry{ + level = error, + msg = "dang" + }, + do_format({ + error_report, + erlang:group_leader(), + {self(), std_error, dang} + }) + ). + + +do_format(Event) -> + E = couch_log_formatter:format(Event), + E#log_entry{ + msg = lists:flatten(E#log_entry.msg), + msg_id = lists:flatten(E#log_entry.msg_id), + time_stamp = lists:flatten(E#log_entry.time_stamp) + }. + + +do_matches(_, []) -> + ok; + +do_matches(#log_entry{msg = Msg} = E, [Pattern | RestPatterns]) -> + case re:run(Msg, Pattern) of + {match, _} -> + ok; + nomatch -> + Err1 = io_lib:format("'~s' does not match '~s'", [Pattern, Msg]), + Err2 = lists:flatten(Err1), + ?assertEqual(nomatch, Err2) + end, + do_matches(E, RestPatterns). http://git-wip-us.apache.org/repos/asf/couchdb-couch-log/blob/16a46572/test/couch_log_monitor_test.erl ---------------------------------------------------------------------- diff --git a/test/couch_log_monitor_test.erl b/test/couch_log_monitor_test.erl new file mode 100644 index 0000000..eec0085 --- /dev/null +++ b/test/couch_log_monitor_test.erl @@ -0,0 +1,67 @@ +% Licensed under the Apache License, Version 2.0 (the "License"); you may not +% use this file except in compliance with the License. You may obtain a copy of +% the License at +% +% http://www.apache.org/licenses/LICENSE-2.0 +% +% Unless required by applicable law or agreed to in writing, software +% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +% License for the specific language governing permissions and limitations under +% the License. + +-module(couch_log_monitor_test). + + +-include_lib("eunit/include/eunit.hrl"). + + +-define(HANDLER, couch_log_error_logger_h). + + +couch_log_monitor_test_() -> + {setup, + fun couch_log_test_util:start/0, + fun couch_log_test_util:stop/1, + [ + fun monitor_ignores_unknown_messages/0, + fun monitor_restarts_handler/0, + fun coverage_test/0 + ] + }. + + +monitor_ignores_unknown_messages() -> + Pid1 = get_monitor_pid(), + + ?assertEqual(ignored, gen_server:call(Pid1, do_foo_please)), + + gen_server:cast(Pid1, do_bar_please), + Pid1 ! do_baz_please, + timer:sleep(250), + ?assert(is_process_alive(Pid1)). + + +monitor_restarts_handler() -> + Pid1 = get_monitor_pid(), + error_logger:delete_report_handler(?HANDLER), + timer:sleep(250), + + ?assert(not is_process_alive(Pid1)), + + Pid2 = get_monitor_pid(), + ?assert(is_process_alive(Pid2)), + + Handlers = gen_event:which_handlers(error_logger), + ?assert(lists:member(?HANDLER, Handlers)). + + +coverage_test() -> + Resp = couch_log_monitor:code_change(foo, bazinga, baz), + ?assertEqual({ok, bazinga}, Resp). + + +get_monitor_pid() -> + Children = supervisor:which_children(couch_log_sup), + [MonPid] = [Pid || {couch_log_monitor, Pid, _, _} <- Children, is_pid(Pid)], + MonPid. http://git-wip-us.apache.org/repos/asf/couchdb-couch-log/blob/16a46572/test/couch_log_server_test.erl ---------------------------------------------------------------------- diff --git a/test/couch_log_server_test.erl b/test/couch_log_server_test.erl new file mode 100644 index 0000000..7af570e --- /dev/null +++ b/test/couch_log_server_test.erl @@ -0,0 +1,118 @@ +% Licensed under the Apache License, Version 2.0 (the "License"); you may not +% use this file except in compliance with the License. You may obtain a copy of +% the License at +% +% http://www.apache.org/licenses/LICENSE-2.0 +% +% Unless required by applicable law or agreed to in writing, software +% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +% License for the specific language governing permissions and limitations under +% the License. + +-module(couch_log_server_test). + + +-include("couch_log.hrl"). +-include_lib("eunit/include/eunit.hrl"). + + +couch_log_server_test_() -> + {setup, + fun couch_log_test_util:start/0, + fun couch_log_test_util:stop/1, + [ + fun check_can_reconfigure/0, + fun check_can_restart/0, + fun check_can_cast_log_entry/0, + fun check_logs_ignored_messages/0 + ] + }. + + +check_can_reconfigure() -> + couch_log:error("a message", []), + ?assertEqual(0, couch_log_test_util:last_log_key()), + ?assertEqual(ok, couch_log_server:reconfigure()), + ?assertEqual('$end_of_table', couch_log_test_util:last_log_key()), + + couch_log_test_util:with_config_listener(fun() -> + couch_log:error("another message", []), + ?assertEqual(0, couch_log_test_util:last_log_key()), + config:set("log", "some_key", "some_val"), + couch_log_test_util:wait_for_config(), + ?assertEqual('$end_of_table', couch_log_test_util:last_log_key()) + end). + + +check_can_restart() -> + Pid1 = whereis(couch_log_server), + Ref = erlang:monitor(process, Pid1), + ?assert(is_process_alive(Pid1)), + + supervisor:terminate_child(couch_log_sup, couch_log_server), + supervisor:restart_child(couch_log_sup, couch_log_server), + + receive + {'DOWN', Ref, _, _, _} -> ok + after 1000 -> + erlang:error(timeout_restarting_couch_log_server) + end, + + ?assert(not is_process_alive(Pid1)), + + Pid2 = whereis(couch_log_server), + ?assertNotEqual(Pid2, Pid1), + ?assert(is_process_alive(Pid2)). + + +check_can_cast_log_entry() -> + Entry = #log_entry{ + level = critical, + pid = self(), + msg = "this will be casted", + msg_id = "----", + time_stamp = "2016-07-20-almost-my-birthday" + }, + ok = gen_server:cast(couch_log_server, {log, Entry}), + timer:sleep(500), % totes gross + ?assertEqual(Entry, couch_log_test_util:last_log()). + + +check_logs_ignored_messages() -> + gen_server:call(couch_log_server, a_call), + ?assertMatch( + #log_entry{ + level = error, + pid = couch_log_server, + msg = "couch_log_server ignored a_call" + }, + couch_log_test_util:last_log() + ), + + gen_server:cast(couch_log_server, a_cast), + timer:sleep(500), % yes gross + ?assertMatch( + #log_entry{ + level = error, + pid = couch_log_server, + msg = "couch_log_server ignored a_cast" + }, + couch_log_test_util:last_log() + ), + + couch_log_server ! an_info, + timer:sleep(500), % still gross + ?assertMatch( + #log_entry{ + level = error, + pid = couch_log_server, + msg = "couch_log_server ignored an_info" + }, + couch_log_test_util:last_log() + ). + + +coverage_test() -> + Resp = couch_log_server:code_change(foo, bazinga, baz), + ?assertEqual({ok, bazinga}, Resp). http://git-wip-us.apache.org/repos/asf/couchdb-couch-log/blob/16a46572/test/couch_log_test.erl ---------------------------------------------------------------------- diff --git a/test/couch_log_test.erl b/test/couch_log_test.erl new file mode 100644 index 0000000..1777730 --- /dev/null +++ b/test/couch_log_test.erl @@ -0,0 +1,85 @@ +% Licensed under the Apache License, Version 2.0 (the "License"); you may not +% use this file except in compliance with the License. You may obtain a copy of +% the License at +% +% http://www.apache.org/licenses/LICENSE-2.0 +% +% Unless required by applicable law or agreed to in writing, software +% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +% License for the specific language governing permissions and limitations under +% the License. + +-module(couch_log_test). + + +-include_lib("couch_log/include/couch_log.hrl"). +-include_lib("eunit/include/eunit.hrl"). + + +couch_log_test_() -> + {setup, + fun couch_log_test_util:start/0, + fun couch_log_test_util:stop/1, + gen() ++ [fun check_set_level/0] + }. + + +check_set_level() -> + couch_log:set_level(crit), + ?assertEqual("crit", config:get("log", "level")). + + +levels() -> + [ + debug, + info, + notice, + warning, + error, + critical, + alert, + emergency, + none + ]. + + +gen() -> + lists:map(fun(L) -> + Name = "Test log level: " ++ couch_log_util:level_to_string(L), + {Name, fun() -> check_levels(L, levels()) end} + end, levels() -- [none]). + + +check_levels(_, []) -> + ok; + +check_levels(TestLevel, [CfgLevel | RestLevels]) -> + TestInt = couch_log_util:level_to_integer(TestLevel), + CfgInt = couch_log_util:level_to_integer(CfgLevel), + Pid = self(), + Msg = new_msg(), + LastKey = couch_log_test_util:last_log_key(), + couch_log_test_util:with_level(CfgLevel, fun() -> + couch_log:TestLevel(Msg, []), + case TestInt >= CfgInt of + true -> + ?assertMatch( + #log_entry{ + level = TestLevel, + pid = Pid, + msg = Msg + }, + couch_log_test_util:last_log() + ); + false -> + ?assertEqual(LastKey, couch_log_test_util:last_log_key()) + end + end), + check_levels(TestLevel, RestLevels). + + +new_msg() -> + random:seed(os:timestamp()), + Bin = list_to_binary([random:uniform(255) || _ <- lists:seq(1, 16)]), + couch_util:to_hex(Bin). http://git-wip-us.apache.org/repos/asf/couchdb-couch-log/blob/16a46572/test/couch_log_test_util.erl ---------------------------------------------------------------------- diff --git a/test/couch_log_test_util.erl b/test/couch_log_test_util.erl new file mode 100644 index 0000000..2503669 --- /dev/null +++ b/test/couch_log_test_util.erl @@ -0,0 +1,153 @@ +% Licensed under the Apache License, Version 2.0 (the "License"); you may not +% use this file except in compliance with the License. You may obtain a copy of +% the License at +% +% http://www.apache.org/licenses/LICENSE-2.0 +% +% Unless required by applicable law or agreed to in writing, software +% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +% License for the specific language governing permissions and limitations under +% the License. + +-module(couch_log_test_util). +-compile(export_all). + + +-include("couch_log.hrl"). + + +start() -> + remove_error_loggers(), + application:set_env(config, ini_files, config_files()), + application:start(config), + ignore_common_loggers(), + application:start(couch_log). + + +stop(_) -> + application:stop(config), + application:stop(couch_log). + + +with_level(Name, Fun) -> + with_config_listener(fun() -> + try + LevelStr = couch_log_util:level_to_string(Name), + config:set("log", "level", LevelStr, false), + wait_for_config(), + Fun() + after + config:delete("log", "level", false) + end + end). + + +with_config_listener(Fun) -> + Listener = self(), + try + add_listener(Listener), + Fun() + after + rem_listener(Listener) + end. + + +wait_for_config() -> + receive + couch_log_config_change_finished -> ok + after 1000 -> + erlang:error(config_change_timeout) + end. + + +with_meck(Mods, Fun) -> + lists:foreach(fun(M) -> + case M of + {Name, Opts} -> meck:new(Name, Opts); + Name -> meck:new(Name) + end + end, Mods), + try + Fun() + after + lists:foreach(fun(M) -> + case M of + {Name, _} -> meck:unload(Name); + Name -> meck:unload(Name) + end + end, Mods) + end. + + +ignore_common_loggers() -> + IgnoreSet = [ + application_controller, + config, + config_event + ], + lists:foreach(fun(Proc) -> + disable_logs_from(Proc) + end, IgnoreSet). + + +disable_logs_from(Pid) when is_pid(Pid) -> + Ignored = case application:get_env(couch_log, ignored_pids) of + {ok, L} when is_list(L) -> + lists:usort([Pid | L]); + _E -> + [Pid] + end, + IgnoredAlive = [P || P <- Ignored, is_process_alive(P)], + application:set_env(couch_log, ignored_pids, IgnoredAlive); + +disable_logs_from(Name) when is_atom(Name) -> + case whereis(Name) of + P when is_pid(P) -> + disable_logs_from(P); + undefined -> + erlang:error({unknown_pid_name, Name}) + end. + + +last_log_key() -> + ets:last(?COUCH_LOG_TEST_TABLE). + + +last_log() -> + [{_, Entry}] = ets:lookup(?COUCH_LOG_TEST_TABLE, last_log_key()), + Entry. + + +remove_error_loggers() -> + lists:foreach(fun(Handler) -> + error_logger:delete_report_handler(Handler) + end, gen_event:which_handlers(error_logger)). + + +config_files() -> + Path = filename:dirname(code:which(?MODULE)), + Name = filename:join(Path, "couch_log_test.ini"), + ok = file:write_file(Name, "[log]\nwriter = ets\n"), + [Name]. + + +add_listener(Listener) -> + Listeners = case application:get_env(couch_log, config_listeners) of + {ok, L} when is_list(L) -> + lists:usort([Listener | L]); + _ -> + [Listener] + end, + application:set_env(couch_log, config_listeners, Listeners). + + +rem_listener(Listener) -> + Listeners = case application:get_env(couch_lig, config_listeners) of + {ok, L} when is_list(L) -> + L -- [Listener]; + _ -> + [] + end, + application:set_env(couch_log, config_listeners, Listeners). + http://git-wip-us.apache.org/repos/asf/couchdb-couch-log/blob/16a46572/test/couch_log_util_test.erl ---------------------------------------------------------------------- diff --git a/test/couch_log_util_test.erl b/test/couch_log_util_test.erl new file mode 100644 index 0000000..e97911a --- /dev/null +++ b/test/couch_log_util_test.erl @@ -0,0 +1,55 @@ +% Licensed under the Apache License, Version 2.0 (the "License"); you may not +% use this file except in compliance with the License. You may obtain a copy of +% the License at +% +% http://www.apache.org/licenses/LICENSE-2.0 +% +% Unless required by applicable law or agreed to in writing, software +% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +% License for the specific language governing permissions and limitations under +% the License. + +-module(couch_log_util_test). + + +-include_lib("couch_log/include/couch_log.hrl"). +-include_lib("eunit/include/eunit.hrl"). + + +get_message_id_test() -> + ?assertEqual("--------", couch_log_util:get_msg_id()), + erlang:put(nonce, "deadbeef"), + ?assertEqual("deadbeef", couch_log_util:get_msg_id()), + erlang:put(nonce, undefined). + + +level_to_atom_test() -> + lists:foreach(fun(L) -> + ?assert(is_atom(couch_log_util:level_to_atom(L))), + ?assert(is_integer(couch_log_util:level_to_integer(L))), + ?assert(is_list(couch_log_util:level_to_string(L))) + end, levels()). + + +string_p_test() -> + ?assertEqual(false, couch_log_util:string_p([])), + ?assertEqual(false, couch_log_util:string_p([[false]])), + ?assertEqual(true, couch_log_util:string_p([$\n])), + ?assertEqual(true, couch_log_util:string_p([$\r])), + ?assertEqual(true, couch_log_util:string_p([$\t])), + ?assertEqual(true, couch_log_util:string_p([$\v])), + ?assertEqual(true, couch_log_util:string_p([$\b])), + ?assertEqual(true, couch_log_util:string_p([$\f])), + ?assertEqual(true, couch_log_util:string_p([$\e])). + + +levels() -> + [ + 1, 2, 3, 4, 5, 6, 7, 8, 9, + "1", "2", "3", "4", "5", "6", "7", "8", "9", + debug, info, notice, warning, warn, error, err, + critical, crit, alert, emergency, emerg, none, + "debug", "info", "notice", "warning", "warn", "error", "err", + "critical", "crit", "alert", "emergency", "emerg", "none" + ]. http://git-wip-us.apache.org/repos/asf/couchdb-couch-log/blob/16a46572/test/couch_log_writer_ets.erl ---------------------------------------------------------------------- diff --git a/test/couch_log_writer_ets.erl b/test/couch_log_writer_ets.erl new file mode 100644 index 0000000..d5fd327 --- /dev/null +++ b/test/couch_log_writer_ets.erl @@ -0,0 +1,49 @@ +% Licensed under the Apache License, Version 2.0 (the "License"); you may not +% use this file except in compliance with the License. You may obtain a copy of +% the License at +% +% http://www.apache.org/licenses/LICENSE-2.0 +% +% Unless required by applicable law or agreed to in writing, software +% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +% License for the specific language governing permissions and limitations under +% the License. + +-module(couch_log_writer_ets). +-behaviour(couch_log_writer). + + +-export([ + init/0, + terminate/2, + write/2 +]). + + +-include("couch_log.hrl"). + + +init() -> + ets:new(?COUCH_LOG_TEST_TABLE, [named_table, public, ordered_set]), + {ok, 0}. + + +terminate(_, _St) -> + ets:delete(?COUCH_LOG_TEST_TABLE), + ok. + + +write(Entry0, St) -> + Entry = Entry0#log_entry{ + msg = lists:flatten(Entry0#log_entry.msg), + time_stamp = lists:flatten(Entry0#log_entry.time_stamp) + }, + Ignored = application:get_env(couch_log, ignored_pids, []), + case lists:member(Entry#log_entry.pid, Ignored) of + true -> + {ok, St}; + false -> + ets:insert(?COUCH_LOG_TEST_TABLE, {St, Entry}), + {ok, St + 1} + end. http://git-wip-us.apache.org/repos/asf/couchdb-couch-log/blob/16a46572/test/couch_log_writer_file_test.erl ---------------------------------------------------------------------- diff --git a/test/couch_log_writer_file_test.erl b/test/couch_log_writer_file_test.erl new file mode 100644 index 0000000..6d3f3ec --- /dev/null +++ b/test/couch_log_writer_file_test.erl @@ -0,0 +1,161 @@ +% Licensed under the Apache License, Version 2.0 (the "License"); you may not +% use this file except in compliance with the License. You may obtain a copy of +% the License at +% +% http://www.apache.org/licenses/LICENSE-2.0 +% +% Unless required by applicable law or agreed to in writing, software +% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +% License for the specific language governing permissions and limitations under +% the License. + +-module(couch_log_writer_file_test). + + +-include_lib("kernel/include/file.hrl"). +-include_lib("couch_log/include/couch_log.hrl"). +-include_lib("eunit/include/eunit.hrl"). + + +-define(WRITER, couch_log_writer_file). + + +couch_log_writer_file_test_() -> + {setup, + fun couch_log_test_util:start/0, + fun couch_log_test_util:stop/1, + [ + fun check_init_terminate/0, + fun() -> + couch_log_test_util:with_meck( + [{filelib, [unstick]}], + fun check_ensure_dir_fail/0 + ) + end, + fun() -> + couch_log_test_util:with_meck( + [{file, [unstick, passthrough]}], + fun check_open_fail/0 + ) + end, + fun() -> + couch_log_test_util:with_meck( + [{file, [unstick, passthrough]}], + fun check_read_file_info_fail/0 + ) + end, + fun check_file_write/0, + fun check_buffered_file_write/0, + fun check_reopen/0 + ] + }. + + +check_init_terminate() -> + {ok, St} = ?WRITER:init(), + ok = ?WRITER:terminate(stop, St). + + +check_ensure_dir_fail() -> + meck:expect(filelib, ensure_dir, 1, {error, eperm}), + ?assertEqual({error, eperm}, ?WRITER:init()), + ?assert(meck:called(filelib, ensure_dir, 1)), + ?assert(meck:validate(filelib)). + + +check_open_fail() -> + meck:expect(file, open, 2, {error, enotfound}), + ?assertEqual({error, enotfound}, ?WRITER:init()), + ?assert(meck:called(file, open, 2)), + ?assert(meck:validate(file)). + + +check_read_file_info_fail() -> + RFI = fun + ("./couch.log") -> {error, enoent}; + (Path) -> meck:passthrough([Path]) + end, + meck:expect(file, read_file_info, RFI), + ?assertEqual({error, enoent}, ?WRITER:init()), + ?assert(meck:called(file, read_file_info, 1)), + ?assert(meck:validate(file)). + + +check_file_write() -> + % Make sure we have an empty log for this test + IsFile = filelib:is_file("./couch.log"), + if not IsFile -> ok; true -> + file:delete("./couch.log") + end, + + Entry = #log_entry{ + level = info, + pid = list_to_pid("<0.1.0>"), + msg = "stuff", + msg_id = "msg_id", + time_stamp = "time_stamp" + }, + {ok, St} = ?WRITER:init(), + {ok, NewSt} = ?WRITER:write(Entry, St), + ok = ?WRITER:terminate(stop, NewSt), + + {ok, Data} = file:read_file("./couch.log"), + Expect = <<"[info] time_stamp nonode@nohost <0.1.0> msg_id stuff\n">>, + ?assertEqual(Expect, Data). + + +check_buffered_file_write() -> + % Make sure we have an empty log for this test + IsFile = filelib:is_file("./couch.log"), + if not IsFile -> ok; true -> + file:delete("./couch.log") + end, + + config:set("log", "write_buffer", "1024"), + config:set("log", "write_delay", "10"), + + try + Entry = #log_entry{ + level = info, + pid = list_to_pid("<0.1.0>"), + msg = "stuff", + msg_id = "msg_id", + time_stamp = "time_stamp" + }, + {ok, St} = ?WRITER:init(), + {ok, NewSt} = ?WRITER:write(Entry, St), + ok = ?WRITER:terminate(stop, NewSt) + after + config:delete("log", "write_buffer"), + config:delete("log", "write_delay") + end, + + {ok, Data} = file:read_file("./couch.log"), + Expect = <<"[info] time_stamp nonode@nohost <0.1.0> msg_id stuff\n">>, + ?assertEqual(Expect, Data). + + +check_reopen() -> + {ok, St1} = clear_clock(?WRITER:init()), + {ok, St2} = clear_clock(couch_log_writer_file:maybe_reopen(St1)), + ?assertEqual(St1, St2), + + % Delete file + file:delete("./couch.log"), + {ok, St3} = clear_clock(couch_log_writer_file:maybe_reopen(St2)), + ?assert(element(3, St3) /= element(3, St2)), + + % Recreate file + file:delete("./couch.log"), + file:write_file("./couch.log", ""), + {ok, St4} = clear_clock(couch_log_writer_file:maybe_reopen(St3)), + ?assert(element(3, St4) /= element(3, St2)). + + +clear_clock({ok, St}) -> + {ok, clear_clock(St)}; + +clear_clock(St) -> + {st, Path, Fd, INode, _} = St, + {st, Path, Fd, INode, {0, 0, 0}}. http://git-wip-us.apache.org/repos/asf/couchdb-couch-log/blob/16a46572/test/couch_log_writer_stderr_test.erl ---------------------------------------------------------------------- diff --git a/test/couch_log_writer_stderr_test.erl b/test/couch_log_writer_stderr_test.erl new file mode 100644 index 0000000..1e99263 --- /dev/null +++ b/test/couch_log_writer_stderr_test.erl @@ -0,0 +1,58 @@ +% Licensed under the Apache License, Version 2.0 (the "License"); you may not +% use this file except in compliance with the License. You may obtain a copy of +% the License at +% +% http://www.apache.org/licenses/LICENSE-2.0 +% +% Unless required by applicable law or agreed to in writing, software +% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +% License for the specific language governing permissions and limitations under +% the License. + +-module(couch_log_writer_stderr_test). + + +-include_lib("couch_log/include/couch_log.hrl"). +-include_lib("eunit/include/eunit.hrl"). + + +-define(WRITER, couch_log_writer_stderr). + + +couch_log_writer_stderr_test_() -> + {setup, + fun couch_log_test_util:start/0, + fun couch_log_test_util:stop/1, + [ + fun check_init_terminate/0, + fun() -> + couch_log_test_util:with_meck( + [{io, [unstick]}], + fun check_write/0 + ) + end + ] + }. + + +check_init_terminate() -> + {ok, St} = ?WRITER:init(), + ok = ?WRITER:terminate(stop, St). + + +check_write() -> + meck:expect(io, format, 3, ok), + + Entry = #log_entry{ + level = debug, + pid = list_to_pid("<0.1.0>"), + msg = "stuff", + msg_id = "msg_id", + time_stamp = "time_stamp" + }, + {ok, St} = ?WRITER:init(), + {ok, NewSt} = ?WRITER:write(Entry, St), + ok = ?WRITER:terminate(stop, NewSt), + + ?assert(meck:validate(io)). http://git-wip-us.apache.org/repos/asf/couchdb-couch-log/blob/16a46572/test/couch_log_writer_syslog_test.erl ---------------------------------------------------------------------- diff --git a/test/couch_log_writer_syslog_test.erl b/test/couch_log_writer_syslog_test.erl new file mode 100644 index 0000000..c32b5c6 --- /dev/null +++ b/test/couch_log_writer_syslog_test.erl @@ -0,0 +1,122 @@ +% Licensed under the Apache License, Version 2.0 (the "License"); you may not +% use this file except in compliance with the License. You may obtain a copy of +% the License at +% +% http://www.apache.org/licenses/LICENSE-2.0 +% +% Unless required by applicable law or agreed to in writing, software +% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +% License for the specific language governing permissions and limitations under +% the License. + +-module(couch_log_writer_syslog_test). + + +-include_lib("couch_log/include/couch_log.hrl"). +-include_lib("eunit/include/eunit.hrl"). + + +-define(WRITER, couch_log_writer_syslog). + + +couch_log_writer_syslog_test_() -> + {setup, + fun couch_log_test_util:start/0, + fun couch_log_test_util:stop/1, + [ + fun check_init_terminate/0, + fun() -> + couch_log_test_util:with_meck( + [{io, [unstick]}], + fun check_stderr_write/0 + ) + end, + fun() -> + couch_log_test_util:with_meck( + [{gen_udp, [unstick]}], + fun check_udp_send/0 + ) + end + ] + }. + + +check_init_terminate() -> + {ok, St} = ?WRITER:init(), + ok = ?WRITER:terminate(stop, St). + + +check_stderr_write() -> + meck:expect(io, format, 3, ok), + + Entry = #log_entry{ + level = debug, + pid = list_to_pid("<0.1.0>"), + msg = "stuff", + msg_id = "msg_id", + time_stamp = "time_stamp" + }, + {ok, St} = ?WRITER:init(), + {ok, NewSt} = ?WRITER:write(Entry, St), + ok = ?WRITER:terminate(stop, NewSt), + + ?assert(meck:called(io, format, 3)), + ?assert(meck:validate(io)). + + +check_udp_send() -> + meck:expect(gen_udp, open, 1, {ok, socket}), + meck:expect(gen_udp, send, 4, ok), + meck:expect(gen_udp, close, fun(socket) -> ok end), + + config:set("log", "syslog_host", "localhost"), + try + Entry = #log_entry{ + level = debug, + pid = list_to_pid("<0.1.0>"), + msg = "stuff", + msg_id = "msg_id", + time_stamp = "time_stamp" + }, + {ok, St} = ?WRITER:init(), + {ok, NewSt} = ?WRITER:write(Entry, St), + ok = ?WRITER:terminate(stop, NewSt) + after + config:delete("log", "syslog_host") + end, + + ?assert(meck:called(gen_udp, open, 1)), + ?assert(meck:called(gen_udp, send, 4)), + ?assert(meck:called(gen_udp, close, 1)), + ?assert(meck:validate(gen_udp)). + + +facility_test() -> + Names = [ + "kern", "user", "mail", "daemon", "auth", "syslog", "lpr", + "news", "uucp", "clock", "authpriv", "ftp", "ntp", "audit", + "alert", "cron", "local0", "local1", "local2", "local3", + "local4", "local5", "local6", "local7" + ], + lists:foldl(fun(Name, Id) -> + IdStr = lists:flatten(io_lib:format("~w", [Id])), + ?assertEqual(Id bsl 3, couch_log_writer_syslog:get_facility(Name)), + ?assertEqual(Id bsl 3, couch_log_writer_syslog:get_facility(IdStr)), + Id + 1 + end, 0, Names), + ?assertEqual(23 bsl 3, couch_log_writer_syslog:get_facility("foo")), + ?assertEqual(23 bsl 3, couch_log_writer_syslog:get_facility("-1")), + ?assertEqual(23 bsl 3, couch_log_writer_syslog:get_facility("24")). + + +level_test() -> + Levels = [ + emergency, alert, critical, error, + warning, notice, info, debug + ], + lists:foldl(fun(Name, Id) -> + ?assertEqual(Id, couch_log_writer_syslog:get_level(Name)), + Id + 1 + end, 0, Levels), + ?assertEqual(3, couch_log_writer_syslog:get_level(foo)). http://git-wip-us.apache.org/repos/asf/couchdb-couch-log/blob/16a46572/test/couch_log_writer_test.erl ---------------------------------------------------------------------- diff --git a/test/couch_log_writer_test.erl b/test/couch_log_writer_test.erl new file mode 100644 index 0000000..d0bb347 --- /dev/null +++ b/test/couch_log_writer_test.erl @@ -0,0 +1,54 @@ +% Licensed under the Apache License, Version 2.0 (the "License"); you may not +% use this file except in compliance with the License. You may obtain a copy of +% the License at +% +% http://www.apache.org/licenses/LICENSE-2.0 +% +% Unless required by applicable law or agreed to in writing, software +% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +% License for the specific language governing permissions and limitations under +% the License. + +-module(couch_log_writer_test). + + +-include_lib("couch_log/include/couch_log.hrl"). +-include_lib("eunit/include/eunit.hrl"). + + +couch_log_writer_test_() -> + {setup, + fun couch_log_test_util:start/0, + fun couch_log_test_util:stop/1, + [ + fun check_writer_change/0 + ] + }. + + +check_writer_change() -> + % Change to file and back + couch_log_test_util:with_config_listener(fun() -> + config:set("log", "writer", "file"), + couch_log_test_util:wait_for_config(), + ?assertEqual(undefined, ets:info(?COUCH_LOG_TEST_TABLE)), + ?assert(is_pid(whereis(couch_log_server))), + + config:set("log", "writer", "couch_log_writer_ets"), + couch_log_test_util:wait_for_config(), + ?assertEqual(0, ets:info(?COUCH_LOG_TEST_TABLE, size)) + end), + + % Using a bad setting doesn't break things + couch_log_test_util:with_config_listener(fun() -> + config:set("log", "writer", "hopefully not an atom or module"), + couch_log_test_util:wait_for_config(), + ?assertEqual(undefined, ets:info(?COUCH_LOG_TEST_TABLE)), + ?assert(is_pid(whereis(couch_log_server))), + + config:set("log", "writer", "couch_log_writer_ets"), + couch_log_test_util:wait_for_config(), + ?assertEqual(0, ets:info(?COUCH_LOG_TEST_TABLE, size)) + end). +
