From 08100525a5a3d31fd36f7905859d721bc3dbc0a9 Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Thu, 16 Feb 2017 10:44:00 +0800 Subject: [PATCH] Add inflight module and test suite --- src/{emqttd_hook.erl => emqttd_hooks.erl} | 0 src/emqttd_inflight.erl | 94 +++++++++++++++++++++++ src/emqttd_opts.erl | 35 --------- test/emqttd_inflight_SUITE.erl | 50 ++++++++++++ 4 files changed, 144 insertions(+), 35 deletions(-) rename src/{emqttd_hook.erl => emqttd_hooks.erl} (100%) create mode 100644 src/emqttd_inflight.erl delete mode 100644 src/emqttd_opts.erl create mode 100644 test/emqttd_inflight_SUITE.erl diff --git a/src/emqttd_hook.erl b/src/emqttd_hooks.erl similarity index 100% rename from src/emqttd_hook.erl rename to src/emqttd_hooks.erl diff --git a/src/emqttd_inflight.erl b/src/emqttd_inflight.erl new file mode 100644 index 000000000..bb9af390b --- /dev/null +++ b/src/emqttd_inflight.erl @@ -0,0 +1,94 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io) +%% +%% 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. +%%-------------------------------------------------------------------- + +%% @doc Inflight Window that wraps the gb_trees. + +-module(emqttd_inflight). + +-author("Feng Lee "). + +-export([new/1, contain/2, lookup/2, insert/3, update/3, delete/2, values/1, + to_list/1, size/1, max_size/1, is_full/1, is_empty/1, window/1]). + +-type(inflight() :: {?MODULE, list()}). + +-export_type([inflight/0]). + +-spec(new(non_neg_integer()) -> inflight()). +new(MaxSize) when MaxSize >= 0 -> + {?MODULE, [MaxSize, gb_trees:empty()]}. + +-spec(contain(Key :: any(), inflight()) -> boolean()). +contain(Key, {?MODULE, [_MaxSize, Tree]}) -> + gb_trees:is_defined(Key, Tree). + +-spec(lookup(Key :: any(), inflight()) -> any()). +lookup(Key, {?MODULE, [_MaxSize, Tree]}) -> + gb_trees:get(Key, Tree). + +-spec(insert(Key :: any(), Value :: any(), inflight()) -> inflight()). +insert(Key, Value, {?MODULE, [MaxSize, Tree]}) -> + {?MODULE, [MaxSize, gb_trees:insert(Key, Value, Tree)]}. + +-spec(delete(Key :: any(), inflight()) -> inflight()). +delete(Key, {?MODULE, [MaxSize, Tree]}) -> + {?MODULE, [MaxSize, gb_trees:delete(Key, Tree)]}. + +-spec(update(Key :: any(), Val :: any(), inflight()) -> inflight()). +update(Key, Val, {?MODULE, [MaxSize, Tree]}) -> + {?MODULE, [MaxSize, gb_trees:update(Key, Val, Tree)]}. + +-spec(is_full(inflight()) -> boolean()). +is_full({?MODULE, [0, _Tree]}) -> + false; +is_full({?MODULE, [MaxSize, Tree]}) -> + MaxSize =< gb_trees:size(Tree). + +-spec(is_empty(inflight()) -> boolean()). +is_empty({?MODULE, [_MaxSize, Tree]}) -> + gb_trees:is_empty(Tree). + +-spec(smallest(inflight()) -> {K :: any(), V :: any()}). +smallest({?MODULE, [_MaxSize, Tree]}) -> + gb_trees:smallest(Tree). + +-spec(largest(inflight()) -> {K :: any(), V :: any()}). +largest({?MODULE, [_MaxSize, Tree]}) -> + gb_trees:largest(Tree). + +-spec(values(inflight()) -> list()). +values({?MODULE, [_MaxSize, Tree]}) -> + gb_trees:values(Tree). + +-spec(to_list(inflight()) -> list({K :: any(), V :: any()})). +to_list({?MODULE, [_MaxSize, Tree]}) -> + gb_trees:to_list(Tree). + +-spec(window(inflight()) -> list()). +window(Inflight = {?MODULE, [_MaxSize, Tree]}) -> + case gb_trees:is_empty(Tree) of + true -> []; + false -> [Key || {Key, _Val} <- [smallest(Inflight), largest(Inflight)]] + end. + +-spec(size(inflight()) -> non_neg_integer()). +size({?MODULE, [_MaxSize, Tree]}) -> + gb_trees:size(Tree). + +-spec(max_size(inflight()) -> non_neg_integer()). +max_size({?MODULE, [MaxSize, _Tree]}) -> + MaxSize. + diff --git a/src/emqttd_opts.erl b/src/emqttd_opts.erl deleted file mode 100644 index 8f5f3fea5..000000000 --- a/src/emqttd_opts.erl +++ /dev/null @@ -1,35 +0,0 @@ -%%-------------------------------------------------------------------- -%% Copyright (c) 2012-2017 Feng Lee . -%% -%% 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(emqttd_opts). - --export([merge/2]). - -%% @doc Merge Options -merge(Defaults, Options) -> - lists:foldl( - fun({Opt, Val}, Acc) -> - case lists:keymember(Opt, 1, Acc) of - true -> lists:keyreplace(Opt, 1, Acc, {Opt, Val}); - false -> [{Opt, Val}|Acc] - end; - (Opt, Acc) -> - case lists:member(Opt, Acc) of - true -> Acc; - false -> [Opt | Acc] - end - end, Defaults, Options). - diff --git a/test/emqttd_inflight_SUITE.erl b/test/emqttd_inflight_SUITE.erl new file mode 100644 index 000000000..5a87d056d --- /dev/null +++ b/test/emqttd_inflight_SUITE.erl @@ -0,0 +1,50 @@ +%% +%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io) +%% + +-module(emqttd_inflight_SUITE). + +-author("Feng Lee "). + +-include_lib("eunit/include/eunit.hrl"). + +%% CT +-compile(export_all). + +all() -> [t_contain, t_lookup, t_insert, t_update, t_delete, t_window, + t_is_full, t_is_empty]. + +t_contain(_) -> + Inflight = emqttd_inflight:new(0), + ?assertNot(Inflight:contain(k)), + Inflight1 = Inflight:insert(k, v), + ?assert(Inflight1:contain(k)). + +t_lookup(_) -> + Inflight = (emqttd_inflight:new(0)):insert(k, v), + ?assertEqual(v, Inflight:lookup(k)). + +t_insert(_) -> + Inflight = ((emqttd_inflight:new(0)):insert(k1, v1)):insert(k2, v2), + ?assertEqual(v2, Inflight:lookup(k2)). + +t_update(_) -> + Inflight = ((emqttd_inflight:new(0)):insert(k, v1)):update(k, v2), + ?assertEqual(v2, Inflight:lookup(k)). + +t_delete(_) -> + Inflight = ((emqttd_inflight:new(0)):insert(k, v1)):delete(k), + ?assert(Inflight:is_empty()). + +t_window(_) -> + ?assertEqual([], (emqttd_inflight:new(10)):window()), + Inflight = ((emqttd_inflight:new(0)):insert(1, 1)):insert(2, 2), + ?assertEqual([1, 2], Inflight:window()). + +t_is_full(_) -> + Inflight = ((emqttd_inflight:new(1)):insert(k, v1)), + ?assert(Inflight:is_full()). + +t_is_empty(_) -> + Inflight = ((emqttd_inflight:new(1)):insert(k, v1)), + ?assertNot(Inflight:is_empty()).