1%%% 2%%% Copyright 2012 - Basho Technologies, Inc. All Rights Reserved. 3%%% 4%%% Licensed under the Apache License, Version 2.0 (the "License"); 5%%% you may not use this file except in compliance with the License. 6%%% You may obtain a copy of the License at 7%%% 8%%% http://www.apache.org/licenses/LICENSE-2.0 9%%% 10%%% Unless required by applicable law or agreed to in writing, software 11%%% distributed under the License is distributed on an "AS IS" BASIS, 12%%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13%%% See the License for the specific language governing permissions and 14%%% limitations under the License. 15%%% 16 17%%%------------------------------------------------------------------- 18%%% File: folsom_sample_slide.erl 19%%% @author Russell Brown <russelldb@basho.com> 20%%% @doc eunit test for folsom_sample_slide.erl 21%%% @end 22%%%------------------------------------------------------------------ 23 24-module(folsom_sample_slide_test). 25 26-include_lib("eunit/include/eunit.hrl"). 27-include("folsom.hrl"). 28 29-define(HISTO, test_slide). 30-define(HISTO2, test_slide2). 31-define(WINDOW, 30). 32-define(DOUBLE_WINDOW, 60). 33-define(RUNTIME, 90). 34-define(READINGS, 10). 35 36slide_test_() -> 37 {setup, 38 fun () -> {ok, Apps} = application:ensure_all_started(folsom), 39 meck:new(folsom_utils), 40 Apps 41 end, 42 fun (Apps) -> meck:unload(folsom_utils), 43 [application:stop(App) || App <- Apps] 44 end, 45 [{"Create sliding window", 46 fun create/0}, 47 {"test sliding window", 48 {timeout, 30, fun exercise/0}}, 49 {"resize sliding window (expand)", 50 {timeout, 30, fun expand_window/0}}, 51 {"resize sliding window (shrink)", 52 {timeout, 30, fun shrink_window/0}} 53 54 ]}. 55 56create() -> 57 ok = folsom_metrics:new_histogram(?HISTO, slide, ?WINDOW), 58 #histogram{sample=Slide} = folsom_metrics_histogram:get_value(?HISTO), 59 ?assert(is_pid(Slide#slide.server)), 60 ?assertEqual(?WINDOW, Slide#slide.window), 61 ?assertEqual(0, ets:info(Slide#slide.reservoir, size)). 62 63exercise() -> 64 %% don't want a trim to happen 65 %% unless we call trim 66 %% so kill the trim server process 67 #histogram{sample=Slide} = folsom_metrics_histogram:get_value(?HISTO), 68 ok = folsom_sample_slide_server:stop(Slide#slide.server), 69 Moments = lists:seq(1, ?RUNTIME), 70 %% pump in 90 seconds worth of readings 71 Moment = lists:foldl(fun(_X, Tick) -> 72 Tock = tick(Tick), 73 [folsom_sample_slide:update(Slide, N) || 74 N <- lists:duplicate(?READINGS, Tock)], 75 Tock end, 76 0, 77 Moments), 78 %% are all readings in the table? 79 check_table(Slide, Moments), 80 %% get values only returns last ?WINDOW seconds 81 ExpectedValues = lists:sort(lists:flatten([lists:duplicate(?READINGS, N) || 82 N <- lists:seq(?RUNTIME - ?WINDOW, ?RUNTIME)])), 83 Values = lists:sort(folsom_sample_slide:get_values(Slide)), 84 ?assertEqual(ExpectedValues, Values), 85 %% trim the table 86 Trimmed = folsom_sample_slide:trim(Slide#slide.reservoir, ?WINDOW), 87 ?assertEqual((?RUNTIME - ?WINDOW - 1) * ?READINGS, Trimmed), 88 check_table(Slide, lists:seq(?RUNTIME - ?WINDOW, ?RUNTIME)), 89 %% increment the clock past the window 90 tick(Moment, ?WINDOW * 2), 91 %% get values should be empty 92 ?assertEqual([], folsom_sample_slide:get_values(Slide)), 93 %% trim, and table should be empty 94 Trimmed2 = folsom_sample_slide:trim(Slide#slide.reservoir, ?WINDOW), 95 ?assertEqual((?RUNTIME * ?READINGS) - ((?RUNTIME - ?WINDOW - 1) * ?READINGS), Trimmed2), 96 check_table(Slide, []), 97 ok. 98 99expand_window() -> 100 %% create a new histogram 101 %% will leave the trim server running, as resize() needs it 102 ok = folsom_metrics:new_histogram(?HISTO2, slide, ?WINDOW), 103 #histogram{sample=Slide} = folsom_metrics_histogram:get_value(?HISTO2), 104 Moments = lists:seq(1, ?RUNTIME ), 105 %% pump in 90 seconds worth of readings 106 Moment = lists:foldl(fun(_X, Tick) -> 107 Tock = tick(Tick), 108 [folsom_sample_slide:update(Slide, N) || 109 N <- lists:duplicate(?READINGS, Tock)], 110 Tock end, 111 0, 112 Moments), 113 %% are all readings in the table? 114 check_table(Slide, Moments), 115 116 %% get values only returns last ?WINDOW seconds 117 ExpectedValues = lists:sort(lists:flatten([lists:duplicate(?READINGS, N) || 118 N <- lists:seq(?RUNTIME - ?WINDOW, ?RUNTIME)])), 119 Values = lists:sort(folsom_sample_slide:get_values(Slide)), 120 ?assertEqual(ExpectedValues, Values), 121 122 %%expand the sliding window 123 NewSlide = folsom_sample_slide:resize(Slide, ?DOUBLE_WINDOW), 124 125 %% get values only returns last ?WINDOW*2 seconds 126 NewExpectedValues = lists:sort(lists:flatten([lists:duplicate(?READINGS, N) || 127 N <- lists:seq(?RUNTIME - ?DOUBLE_WINDOW, ?RUNTIME)])), 128 NewValues = lists:sort(folsom_sample_slide:get_values(NewSlide)), 129 ?assertEqual(NewExpectedValues, NewValues), 130 131 132 %% trim the table 133 Trimmed = folsom_sample_slide:trim(NewSlide#slide.reservoir, ?DOUBLE_WINDOW), 134 ?assertEqual((?RUNTIME - ?DOUBLE_WINDOW - 1) * ?READINGS, Trimmed), 135 check_table(NewSlide, lists:seq(?RUNTIME - ?DOUBLE_WINDOW, ?RUNTIME)), 136 %% increment the clock past the window 137 tick(Moment, ?DOUBLE_WINDOW*2), 138 %% get values should be empty 139 ?assertEqual([], folsom_sample_slide:get_values(NewSlide)), 140 %% trim, and table should be empty 141 Trimmed2 = folsom_sample_slide:trim(NewSlide#slide.reservoir, ?DOUBLE_WINDOW), 142 ?assertEqual((?RUNTIME * ?READINGS) - ((?RUNTIME - ?DOUBLE_WINDOW - 1) * ?READINGS), Trimmed2), 143 check_table(NewSlide, []), 144 ok = folsom_metrics:delete_metric(?HISTO2). 145 146 147shrink_window() -> 148 %% create a new histogram 149 %% will leave the trim server running, as resize() needs it 150 ok = folsom_metrics:new_histogram(?HISTO2, slide, ?DOUBLE_WINDOW), 151 #histogram{sample=Slide} = folsom_metrics_histogram:get_value(?HISTO2), 152 Moments = lists:seq(1, ?RUNTIME ), 153 %% pump in 90 seconds worth of readings 154 Moment = lists:foldl(fun(_X, Tick) -> 155 Tock = tick(Tick), 156 [folsom_sample_slide:update(Slide, N) || 157 N <- lists:duplicate(?READINGS, Tock)], 158 Tock end, 159 0, 160 Moments), 161 %% are all readings in the table? 162 check_table(Slide, Moments), 163 164 %% get values only returns last ?DOUBLE_WINDOW seconds 165 ExpectedValues = lists:sort(lists:flatten([lists:duplicate(?READINGS, N) || 166 N <- lists:seq(?RUNTIME - ?DOUBLE_WINDOW, ?RUNTIME)])), 167 Values = lists:sort(folsom_sample_slide:get_values(Slide)), 168 ?assertEqual(ExpectedValues, Values), 169 170 %%shrink the sliding window 171 NewSlide = folsom_sample_slide:resize(Slide, ?WINDOW), 172 173 %% get values only returns last ?WINDOW*2 seconds 174 NewExpectedValues = lists:sort(lists:flatten([lists:duplicate(?READINGS, N) || 175 N <- lists:seq(?RUNTIME - ?WINDOW, ?RUNTIME)])), 176 NewValues = lists:sort(folsom_sample_slide:get_values(NewSlide)), 177 ?assertEqual(NewExpectedValues, NewValues), 178 179 180 %% trim the table 181 Trimmed = folsom_sample_slide:trim(NewSlide#slide.reservoir, ?WINDOW), 182 ?assertEqual((?RUNTIME - ?WINDOW - 1) * ?READINGS, Trimmed), 183 check_table(NewSlide, lists:seq(?RUNTIME - ?WINDOW, ?RUNTIME)), 184 %% increment the clock past the window 185 tick(Moment, ?WINDOW*2), 186 %% get values should be empty 187 ?assertEqual([], folsom_sample_slide:get_values(NewSlide)), 188 %% trim, and table should be empty 189 Trimmed2 = folsom_sample_slide:trim(NewSlide#slide.reservoir, ?WINDOW), 190 ?assertEqual((?RUNTIME * ?READINGS) - ((?RUNTIME - ?WINDOW - 1) * ?READINGS), Trimmed2), 191 check_table(NewSlide, []), 192 ok. 193 194tick(Moment0, IncrBy) -> 195 Moment = Moment0 + IncrBy, 196 meck:expect(folsom_utils, now_epoch, fun() -> 197 Moment end), 198 Moment. 199 200tick(Moment) -> 201 tick(Moment, 1). 202 203check_table(Slide, Moments) -> 204 Tab = lists:sort(ets:tab2list(Slide#slide.reservoir)), 205 {Ks, Vs} = lists:unzip(Tab), 206 ExpectedVs = lists:sort(lists:flatten([lists:duplicate(10, N) || N <- Moments])), 207 StrippedKeys = lists:usort([X || {X, _} <- Ks]), 208 ?assertEqual(Moments, StrippedKeys), 209 ?assertEqual(ExpectedVs, lists:sort(Vs)). 210