blob: 762953dddf4f3f09edcddc34284f6856e78b5c18 [file] [log] [blame]
% 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_stats_tests).
-include_lib("couch/include/couch_eunit.hrl").
-include_lib("couch/include/couch_db.hrl").
-define(STATS_CFG_FIXTURE,
filename:join([?FIXTURESDIR, "couch_stats_aggregates.cfg"])).
-define(STATS_INI_FIXTURE,
filename:join([?FIXTURESDIR, "couch_stats_aggregates.ini"])).
-define(TIMEOUT, 1000).
-define(TIMEWAIT, 500).
-ifdef(run_broken_tests).
setup_collector() ->
couch_stats_collector:start(),
ok.
setup_aggregator(_) ->
{ok, Pid} = config:start_link([?STATS_INI_FIXTURE]),
{ok, _} = couch_stats_collector:start(),
{ok, _} = couch_stats_aggregator:start(?STATS_CFG_FIXTURE),
Pid.
teardown_collector(_) ->
couch_stats_collector:stop(),
ok.
teardown_aggregator(_, Pid) ->
couch_stats_aggregator:stop(),
couch_stats_collector:stop(),
test_util:stop_config(Pid).
couch_stats_collector_test_() ->
{
"CouchDB stats collector tests",
{
foreach,
fun setup_collector/0, fun teardown_collector/1,
[
should_increment_counter(),
should_decrement_counter(),
should_increment_and_decrement_counter(),
should_record_absolute_values(),
should_clear_absolute_values(),
should_track_process_count(),
should_increment_counter_multiple_times_per_pid(),
should_decrement_counter_on_process_exit(),
should_decrement_for_each_track_process_count_call_on_exit(),
should_return_all_counters_and_absolute_values(),
should_return_incremental_counters(),
should_return_absolute_values()
]
}
}.
couch_stats_aggregator_test_() ->
Funs = [
fun should_init_empty_aggregate/2,
fun should_get_empty_aggregate/2,
fun should_change_stats_on_values_add/2,
fun should_change_stats_for_all_times_on_values_add/2,
fun should_change_stats_on_values_change/2,
fun should_change_stats_for_all_times_on_values_change/2,
fun should_not_remove_data_after_some_time_for_0_sample/2,
fun should_remove_data_after_some_time_for_other_samples/2
],
{
"CouchDB stats aggregator tests",
[
{
"Absolute values",
{
foreachx,
fun setup_aggregator/1, fun teardown_aggregator/2,
[{absolute, Fun} || Fun <- Funs]
}
},
{
"Counters",
{
foreachx,
fun setup_aggregator/1, fun teardown_aggregator/2,
[{counter, Fun} || Fun <- Funs]
}
}
]
}.
should_increment_counter() ->
?_assertEqual(100,
begin
AddCount = fun() -> couch_stats_collector:increment(foo) end,
repeat(AddCount, 100),
couch_stats_collector:get(foo)
end).
should_decrement_counter() ->
?_assertEqual(67,
begin
AddCount = fun() -> couch_stats_collector:increment(foo) end,
RemCount = fun() -> couch_stats_collector:decrement(foo) end,
repeat(AddCount, 100),
repeat(RemCount, 33),
couch_stats_collector:get(foo)
end).
should_increment_and_decrement_counter() ->
?_assertEqual(0,
begin
AddCount = fun() -> couch_stats_collector:increment(foo) end,
RemCount = fun() -> couch_stats_collector:decrement(foo) end,
repeat(AddCount, 100),
repeat(RemCount, 25),
repeat(AddCount, 10),
repeat(RemCount, 5),
repeat(RemCount, 80),
couch_stats_collector:get(foo)
end).
should_record_absolute_values() ->
?_assertEqual(lists:seq(1, 15),
begin
lists:map(fun(Val) ->
couch_stats_collector:record(bar, Val)
end, lists:seq(1, 15)),
couch_stats_collector:get(bar)
end).
should_clear_absolute_values() ->
?_assertEqual(nil,
begin
lists:map(fun(Val) ->
couch_stats_collector:record(bar, Val)
end, lists:seq(1, 15)),
couch_stats_collector:clear(bar),
couch_stats_collector:get(bar)
end).
should_track_process_count() ->
?_assertMatch({_, 1}, spawn_and_count(1)).
should_increment_counter_multiple_times_per_pid() ->
?_assertMatch({_, 3}, spawn_and_count(3)).
should_decrement_counter_on_process_exit() ->
?_assertEqual(2,
begin
{Pid, 1} = spawn_and_count(1),
spawn_and_count(2),
test_util:stop_sync_throw(Pid,
fun() -> Pid ! sepuku end, timeout, ?TIMEOUT),
% sleep for awhile to let collector handle the updates
% suddenly, it couldn't notice process death instantly
timer:sleep(?TIMEWAIT),
couch_stats_collector:get(hoopla)
end).
should_decrement_for_each_track_process_count_call_on_exit() ->
?_assertEqual(2,
begin
{_, 2} = spawn_and_count(2),
{Pid, 6} = spawn_and_count(4),
test_util:stop_sync_throw(Pid,
fun() -> Pid ! sepuku end, timeout, ?TIMEOUT),
timer:sleep(?TIMEWAIT),
couch_stats_collector:get(hoopla)
end).
should_return_all_counters_and_absolute_values() ->
?_assertEqual([{bar,[1.0,0.0]}, {foo,1}],
begin
couch_stats_collector:record(bar, 0.0),
couch_stats_collector:record(bar, 1.0),
couch_stats_collector:increment(foo),
lists:sort(couch_stats_collector:all())
end).
should_return_incremental_counters() ->
?_assertEqual([{foo,1}],
begin
couch_stats_collector:record(bar, 0.0),
couch_stats_collector:record(bar, 1.0),
couch_stats_collector:increment(foo),
lists:sort(couch_stats_collector:all(incremental))
end).
should_return_absolute_values() ->
?_assertEqual([{bar,[1.0,0.0]}, {zing, "Z"}],
begin
couch_stats_collector:record(bar, 0.0),
couch_stats_collector:record(bar, 1.0),
couch_stats_collector:record(zing, 90),
couch_stats_collector:increment(foo),
lists:sort(couch_stats_collector:all(absolute))
end).
should_init_empty_aggregate(absolute, _) ->
{Aggs} = couch_stats_aggregator:all(),
?_assertEqual({[{'11', make_agg(<<"randomosity">>,
null, null, null, null, null)}]},
couch_util:get_value(number, Aggs));
should_init_empty_aggregate(counter, _) ->
{Aggs} = couch_stats_aggregator:all(),
?_assertEqual({[{stuff, make_agg(<<"yay description">>,
null, null, null, null, null)}]},
couch_util:get_value(testing, Aggs)).
should_get_empty_aggregate(absolute, _) ->
?_assertEqual(make_agg(<<"randomosity">>, null, null, null, null, null),
couch_stats_aggregator:get_json({number, '11'}));
should_get_empty_aggregate(counter, _) ->
?_assertEqual(make_agg(<<"yay description">>, null, null, null, null, null),
couch_stats_aggregator:get_json({testing, stuff})).
should_change_stats_on_values_add(absolute, _) ->
lists:foreach(fun(X) ->
couch_stats_collector:record({number, 11}, X)
end, lists:seq(0, 10)),
couch_stats_aggregator:collect_sample(),
?_assertEqual(make_agg(<<"randomosity">>, 5.0, 5.0, null, 5.0, 5.0),
couch_stats_aggregator:get_json({number, 11}));
should_change_stats_on_values_add(counter, _) ->
lists:foreach(fun(_) ->
couch_stats_collector:increment({testing, stuff})
end, lists:seq(1, 100)),
couch_stats_aggregator:collect_sample(),
?_assertEqual(make_agg(<<"yay description">>, 100.0, 100.0, null, 100, 100),
couch_stats_aggregator:get_json({testing, stuff})).
should_change_stats_for_all_times_on_values_add(absolute, _) ->
lists:foreach(fun(X) ->
couch_stats_collector:record({number, 11}, X)
end, lists:seq(0, 10)),
couch_stats_aggregator:collect_sample(),
?_assertEqual(make_agg(<<"randomosity">>, 5.0, 5.0, null, 5.0, 5.0),
couch_stats_aggregator:get_json({number, 11}, 1));
should_change_stats_for_all_times_on_values_add(counter, _) ->
lists:foreach(fun(_) ->
couch_stats_collector:increment({testing, stuff})
end, lists:seq(1, 100)),
couch_stats_aggregator:collect_sample(),
?_assertEqual(make_agg(<<"yay description">>, 100.0, 100.0, null, 100, 100),
couch_stats_aggregator:get_json({testing, stuff}, 1)).
should_change_stats_on_values_change(absolute, _) ->
?_assertEqual(make_agg(<<"randomosity">>, 20.0, 10.0, 7.071, 5.0, 15.0),
begin
lists:foreach(fun(X) ->
couch_stats_collector:record({number, 11}, X)
end, lists:seq(0, 10)),
couch_stats_aggregator:collect_sample(),
timer:sleep(?TIMEWAIT),
couch_stats_collector:record({number, 11}, 15),
couch_stats_aggregator:collect_sample(),
couch_stats_aggregator:get_json({number, 11})
end);
should_change_stats_on_values_change(counter, _) ->
?_assertEqual(make_agg(<<"yay description">>, 100.0, 50.0, 70.711, 0, 100),
begin
lists:foreach(fun(_) ->
couch_stats_collector:increment({testing, stuff})
end, lists:seq(1, 100)),
couch_stats_aggregator:collect_sample(),
timer:sleep(?TIMEWAIT),
couch_stats_aggregator:collect_sample(),
couch_stats_aggregator:get_json({testing, stuff})
end).
should_change_stats_for_all_times_on_values_change(absolute, _) ->
?_assertEqual(make_agg(<<"randomosity">>, 20.0, 10.0, 7.071, 5.0, 15.0),
begin
lists:foreach(fun(X) ->
couch_stats_collector:record({number, 11}, X)
end, lists:seq(0, 10)),
couch_stats_aggregator:collect_sample(),
timer:sleep(?TIMEWAIT),
couch_stats_collector:record({number, 11}, 15),
couch_stats_aggregator:collect_sample(),
couch_stats_aggregator:get_json({number, 11}, 1)
end);
should_change_stats_for_all_times_on_values_change(counter, _) ->
?_assertEqual(make_agg(<<"yay description">>, 100.0, 50.0, 70.711, 0, 100),
begin
lists:foreach(fun(_) ->
couch_stats_collector:increment({testing, stuff})
end, lists:seq(1, 100)),
couch_stats_aggregator:collect_sample(),
timer:sleep(?TIMEWAIT),
couch_stats_aggregator:collect_sample(),
couch_stats_aggregator:get_json({testing, stuff}, 1)
end).
should_not_remove_data_after_some_time_for_0_sample(absolute, _) ->
?_assertEqual(make_agg(<<"randomosity">>, 20.0, 10.0, 7.071, 5.0, 15.0),
begin
lists:foreach(fun(X) ->
couch_stats_collector:record({number, 11}, X)
end, lists:seq(0, 10)),
couch_stats_aggregator:collect_sample(),
timer:sleep(?TIMEWAIT),
couch_stats_collector:record({number, 11}, 15),
couch_stats_aggregator:collect_sample(),
timer:sleep(?TIMEWAIT),
couch_stats_aggregator:collect_sample(),
couch_stats_aggregator:get_json({number, 11})
end);
should_not_remove_data_after_some_time_for_0_sample(counter, _) ->
?_assertEqual(make_agg(<<"yay description">>, 100.0, 33.333, 57.735, 0, 100),
begin
lists:foreach(fun(_) ->
couch_stats_collector:increment({testing, stuff})
end, lists:seq(1, 100)),
couch_stats_aggregator:collect_sample(),
timer:sleep(?TIMEWAIT),
couch_stats_aggregator:collect_sample(),
timer:sleep(?TIMEWAIT),
couch_stats_aggregator:collect_sample(),
couch_stats_aggregator:get_json({testing, stuff})
end).
should_remove_data_after_some_time_for_other_samples(absolute, _) ->
?_assertEqual(make_agg(<<"randomosity">>, 15.0, 15.0, null, 15.0, 15.0),
begin
lists:foreach(fun(X) ->
couch_stats_collector:record({number, 11}, X)
end, lists:seq(0, 10)),
couch_stats_aggregator:collect_sample(),
timer:sleep(?TIMEWAIT),
couch_stats_collector:record({number, 11}, 15),
couch_stats_aggregator:collect_sample(),
timer:sleep(?TIMEWAIT),
couch_stats_aggregator:collect_sample(),
couch_stats_aggregator:get_json({number, 11}, 1)
end);
should_remove_data_after_some_time_for_other_samples(counter, _) ->
?_assertEqual(make_agg(<<"yay description">>, 0, 0.0, 0.0, 0, 0),
begin
lists:foreach(fun(_) ->
couch_stats_collector:increment({testing, stuff})
end, lists:seq(1, 100)),
couch_stats_aggregator:collect_sample(),
timer:sleep(?TIMEWAIT),
couch_stats_aggregator:collect_sample(),
timer:sleep(?TIMEWAIT),
couch_stats_aggregator:collect_sample(),
couch_stats_aggregator:get_json({testing, stuff}, 1)
end).
spawn_and_count(N) ->
Self = self(),
Pid = spawn(fun() ->
lists:foreach(
fun(_) ->
couch_stats_collector:track_process_count(hoopla)
end, lists:seq(1,N)),
Self ! reporting,
receive
sepuku -> ok
end
end),
receive reporting -> ok end,
{Pid, couch_stats_collector:get(hoopla)}.
repeat(_, 0) ->
ok;
repeat(Fun, Count) ->
Fun(),
repeat(Fun, Count-1).
make_agg(Desc, Sum, Mean, StdDev, Min, Max) ->
{[
{description, Desc},
{current, Sum},
{sum, Sum},
{mean, Mean},
{stddev, StdDev},
{min, Min},
{max, Max}
]}.
-endif.