1: %%==============================================================================
    2: %% Copyright 2017 Erlang Solutions Ltd.
    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: -module(monitored_map_SUITE).
   17: 
   18: -compile([export_all, nowarn_export_all]).
   19: 
   20: -include_lib("common_test/include/ct.hrl").
   21: -include_lib("eunit/include/eunit.hrl").
   22: 
   23: %%--------------------------------------------------------------------
   24: %% Suite configuration
   25: %%--------------------------------------------------------------------
   26: 
   27: all() ->
   28:     [
   29:      no_key_test,
   30:      put_monitors_test,
   31:      get_test,
   32:      remove_test,
   33:      expire_entries_on_process_death_test,
   34:      overwrite_entry_test,
   35:      overwrite_entry_same_pid_test,
   36:      find_test,
   37:      get_default_test,
   38:      remove_nonexistent_element_test,
   39:      ignore_nonrelevant_info_test
   40:     ].
   41: 
   42: %%--------------------------------------------------------------------
   43: %% Tests
   44: %%--------------------------------------------------------------------
   45: 
   46: no_key_test(_Config) ->
   47:     Map = monitored_map:new(),
   48:     ?assertError(_, monitored_map:get(key, Map)).
   49: 
   50: put_monitors_test(_Config) ->
   51:     Map0 = monitored_map:new(),
   52:     Pid = new_process(),
   53:     _Map1 = monitored_map:put(key, value, Pid, Map0),
   54:     assert_monitors([Pid]).
   55: 
   56: get_test(_Config) ->
   57:     Map0 = monitored_map:new(),
   58:     Map1 = monitored_map:put(key, value, self(), Map0),
   59:     ?assertEqual(value, monitored_map:get(key, Map1)).
   60: 
   61: remove_test(_Config) ->
   62:     Map0 = monitored_map:new(),
   63:     Map1 = monitored_map:put(key, value, self(), Map0),
   64:     Map2 = monitored_map:remove(key, Map1),
   65:     ?assertError(_, monitored_map:get(key, Map2)),
   66:     assert_monitors([]).
   67: 
   68: expire_entries_on_process_death_test(_Config) ->
   69:     Map0 = monitored_map:new(),
   70:     Pid1 = spawn(fun() -> ok end),
   71:     Pid2 = spawn(fun() -> ok end),
   72:     Pid3 = new_process(),
   73: 
   74:     Map1 = lists:foldl(fun({Key, Value, Pid}, M) -> monitored_map:put(Key, Value, Pid, M) end,
   75:                        Map0, [{key1, value1, Pid1}, {key2, value2, Pid1},
   76:                               {key3, value3, Pid2}, {key4, value4, Pid3}]),
   77: 
   78:     %% Receive 3 expiration messages: two from first process, one from second
   79:     Map2 = lists:foldl(
   80:              fun(_, Map) -> receive DownMsg1 -> monitored_map:handle_info(DownMsg1, Map) end end,
   81:              Map1, lists:seq(1, 3)),
   82: 
   83:     ?assertError(_, monitored_map:get(key1, Map2)),
   84:     ?assertError(_, monitored_map:get(key2, Map2)),
   85:     ?assertError(_, monitored_map:get(key3, Map2)),
   86:     ?assertEqual(value4, monitored_map:get(key4, Map2)),
   87:     assert_monitors([Pid3]).
   88: 
   89: overwrite_entry_test(_Config) ->
   90:     Map0 = monitored_map:new(),
   91:     Pid = new_process(),
   92:     Map1 = monitored_map:put(key, value1, self(), Map0),
   93:     Map2 = monitored_map:put(key, value2, Pid, Map1),
   94:     ?assertEqual(value2, monitored_map:get(key, Map2)),
   95:     assert_monitors([Pid]).
   96: 
   97: overwrite_entry_same_pid_test(_Config) ->
   98:     Map0 = monitored_map:new(),
   99:     Map1 = monitored_map:put(key, value1, self(), Map0),
  100:     Map2 = monitored_map:put(key, value2, self(), Map1),
  101:     ?assertEqual(value2, monitored_map:get(key, Map2)).
  102: 
  103: find_test(_Config) ->
  104:     Map0 = monitored_map:new(),
  105:     Map1 = monitored_map:put(key, value, self(), Map0),
  106:     ?assertEqual({ok, value}, monitored_map:find(key, Map1)),
  107:     ?assertEqual(error, monitored_map:find(key2, Map1)).
  108: 
  109: get_default_test(_Config) ->
  110:     Map0 = monitored_map:new(),
  111:     Map1 = monitored_map:put(key, value, self(), Map0),
  112:     ?assertEqual(value, monitored_map:get(key, Map1, default_value)),
  113:     ?assertEqual(default_value, monitored_map:get(key2, Map1, default_value)).
  114: 
  115: remove_nonexistent_element_test(_Config) ->
  116:     Map = monitored_map:new(),
  117:     ?assertEqual(Map, monitored_map:remove(key, Map)).
  118: 
  119: ignore_nonrelevant_info_test(_Config) ->
  120:     Map0 = monitored_map:new(),
  121:     Map1 = monitored_map:put(key, value, self(), Map0),
  122:     DownMsg = {'DOWN', make_ref(), process, self(), error},
  123:     ?assertEqual(Map1, monitored_map:handle_info(DownMsg, Map1)),
  124:     ?assertEqual(Map1, monitored_map:handle_info(any_other_msg, Map1)).
  125: 
  126: %%--------------------------------------------------------------------
  127: %% Test helpers
  128: %%--------------------------------------------------------------------
  129: 
  130: assert_monitors(Expected) ->
  131:     {monitors, All} = process_info(self(), monitors),
  132:     MonitoredPids = [Pid || {process, Pid} <- All],
  133:     ?assertEqual(ordsets:from_list(Expected), ordsets:from_list(MonitoredPids)).
  134: 
  135: new_process() ->
  136:     spawn_link(fun() -> receive Anything -> Anything end end).