File katana-0.4.0-git.patch of Package katana

diff --git a/README.md b/README.md
index a93d94b..d7cd4cb 100644
--- a/README.md
+++ b/README.md
@@ -16,9 +16,6 @@ To sum up this is a grab bag of useful functions (ideally).
 
 # Contact Us
 
-For **questions** or **general comments** regarding the use of this library,
-please use our public [hipchat room](https://inaka.net/hipchat).
-
 If you find any **bugs** or have a **problem** while using this library, please
 [open an issue](https://github.com/inaka/erlang-katana/issues/new) in this repo
 (or a pull request :)).
@@ -37,7 +34,6 @@ Check out as well [katana-code](https://github.com/inaka/katana-code) and [katan
 
 * `ktn_date`: functions useful for handling dates and time values.
 * `ktn_debug`: functions useful for debugging.
-* `ktn_fsm`: a _nice_ wrapper on top of `gen_fsm`
 * `ktn_json`: functions useful for processing & creating JSON.
 * `ktn_maps`: functions useful for handling maps.
 * `ktn_numbers`: functions useful for processing numeric values.
@@ -46,6 +42,28 @@ Check out as well [katana-code](https://github.com/inaka/katana-code) and [katan
 * `ktn_task`: functions useful for managing asyncronous tasks.
 * `ktn_user_default`: useful functions for your erlang shell.
 
+### `ktn_date`
+
+This module contains functions to manipulate date and time values.
+
+#### `shift_days`
+
+With `shift_days(Datetime :: calendar:datetime(), N :: integer())` you can move the date expressed in `DateTime`, `N` days to the future or to the past.
+
+#### `shift_months`
+
+With `shift_months(Date :: calendar:date(), N :: integer())` you can move the date expressed in `Date`, `N` months to the future or to the past.
+
+Examples:
+```erlang
+$> ktn_date:shift_months({2018, 8, 15}, 2).
+{2018, 10, 15}
+$> ktn_date:shift_months({2018, 8, 15}, -2).
+{2018, 6,  15}
+$> ktn_date:shift_months({2018, 8, 15}, 6).
+{2019, 2,  15}
+```
+
 ### `ktn_user_default`
 
 This module contains functions that are nice to have in your user default
@@ -142,12 +160,12 @@ all cases.
 A better way to structure the code would be preferable, one that makes the
 flow obvious and separates responsibilities into appropriate code segments.
 One way is a finite state machine. OTP even has a behaviour for exactly this
-purpose. However, gen_fsm does not exactly fit our needs:
-To begin with, gen_fsms run in their own process, which may not be what is
-needed. Second, logic flow is not immediately obvious because the fsm's
-state depends on the result of each state function. Finally, a gen_fsm
+purpose. However, gen_statem does not exactly fit our needs:
+To begin with, gen_statems run in their own process, which may not be what is
+needed. Second, logic flow is not immediately obvious because the FSM's
+state depends on the result of each state function. Finally, a gen_statem
 transitions only on receiving input, whereas we are looking for something
-that runs like normal code, "on its own". So, our fsm will be defined by
+that runs like normal code, "on its own". So, our FSM will be defined by
 something like a transition table, a single place you can look at and know
 how it executes. This specification will "drive" the recipe.
 
diff --git a/rebar.config b/rebar.config
index 3e0d5eb..fdddd2a 100644
--- a/rebar.config
+++ b/rebar.config
@@ -23,11 +23,6 @@
 {profiles, [
   {test, [
     {deps, [{xref_runner, "1.0.0"}]}
-  ]},
-  {shell, [
-    {deps, [
-      {sync, {git, "https://github.com/rustyio/sync.git", {ref, "9c78e7b"}}}
-    ]}
   ]}
 ]}.
 
@@ -59,7 +54,7 @@
 
 %% == Dependencies ==
 
-{deps, [{elvis, "0.3.5", {pkg, elvis_core}}]}.
+{deps, [{elvis, "0.3.9", {pkg, elvis_core}}]}.
 
 %% == Dialyzer ==
 
@@ -72,7 +67,3 @@
            , {plt_location, local}
            , {base_plt_apps, [stdlib, kernel]}
            , {base_plt_location, global}]}.
-
-%% == Shell ==
-
-{shell, [{apps, [sync]}]}.
\ No newline at end of file
diff --git a/rebar.lock b/rebar.lock
index f6c20bf..0f74d44 100644
--- a/rebar.lock
+++ b/rebar.lock
@@ -1,16 +1,12 @@
 {"1.1.0",
 [{<<"aleppo">>,{pkg,<<"inaka_aleppo">>,<<"1.0.0">>},2},
- {<<"elvis">>,{pkg,<<"elvis_core">>,<<"0.3.5">>},0},
- {<<"goldrush">>,{pkg,<<"goldrush">>,<<"0.1.9">>},2},
+ {<<"elvis">>,{pkg,<<"elvis_core">>,<<"0.3.9">>},0},
  {<<"katana_code">>,{pkg,<<"katana_code">>,<<"0.1.0">>},1},
- {<<"lager">>,{pkg,<<"lager">>,<<"3.2.4">>},1},
  {<<"zipper">>,{pkg,<<"zipper">>,<<"1.0.1">>},1}]}.
 [
 {pkg_hash,[
  {<<"aleppo">>, <<"8DB14CF16BB8C263C14FF4C3F69F64D7C849D40888944F4204D2CA74F1114CEB">>},
- {<<"elvis">>, <<"9C6DE2DA5317081D12512CA34EC9CAC858D3A169E6882E86BFAC97FA47962C4D">>},
- {<<"goldrush">>, <<"F06E5D5F1277DA5C413E84D5A2924174182FB108DABB39D5EC548B27424CD106">>},
+ {<<"elvis">>, <<"6B3C18E1E1CC9BC5684282F59A95B6B42898C1A27E0E2F3CFE9B384AF76A7629">>},
  {<<"katana_code">>, <<"C34F3926A258D6BEACD8D21F140F3D47D175501936431C460B144339D5271A0B">>},
- {<<"lager">>, <<"A6DEB74DAE7927F46BD13255268308EF03EB206EC784A94EAF7C1C0F3B811615">>},
  {<<"zipper">>, <<"3CCB4F14B97C06B2749B93D8B6C204A1ECB6FAFC6050CACC3B93B9870C05952A">>}]}
 ].
diff --git a/src/ktn_date.erl b/src/ktn_date.erl
index 688ba59..701e67e 100644
--- a/src/ktn_date.erl
+++ b/src/ktn_date.erl
@@ -3,8 +3,12 @@
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 -module(ktn_date).
 
+-define(SECONDS_IN_A_DAY, 86400).
+
 -export([
-         now_human_readable/0
+         now_human_readable/0,
+         shift_days/2,
+         shift_months/2
         ]).
 
 -type date()     :: {date, {non_neg_integer(), 1..12, 1..31}}.
@@ -27,3 +31,39 @@ now_human_readable() ->
     DateList = io_lib:format("~p-~2..0B-~2..0BT~p:~p:~p.~6..0wZ",
                              [Year, Month, Day, Hour, Minute, Second, Micro]),
     list_to_binary(DateList).
+
+%% @doc Moves the received datetime `N' days to the future (or to the past)
+-spec shift_days(calendar:datetime(), integer()) -> calendar:datetime().
+shift_days(Datetime, N) ->
+    Shift = ?SECONDS_IN_A_DAY * N,
+    Secs = calendar:datetime_to_gregorian_seconds(Datetime),
+    calendar:gregorian_seconds_to_datetime(Secs + Shift).
+
+%% @doc Moves the received date `N' months to the future (or to the past)
+-spec shift_months(calendar:date(), integer()) -> calendar:date().
+shift_months({Y, M, D}, N) ->
+    %% in order for the modular arithmetic to work, months in this function
+    %% range from 0 to 11 (January to December)
+    TotalMonths = 12*Y + M-1 + N,
+
+    case TotalMonths >= 0 of
+        true ->
+            Month = TotalMonths rem 12,
+            Year = (TotalMonths - Month) div 12,
+
+            %% add one back to the month to fix our tricky mod 12
+            find_valid_date({Year, Month+1, D});
+        false ->
+            error(out_of_bounds)
+    end.
+
+%% @doc Returns `Date' if valid. Otherwise, returns `Date' replacing `Day`
+%% with the last day of the month.
+find_valid_date(Date) ->
+    case calendar:valid_date(Date) of
+        true ->
+            Date;
+        false ->
+            {Y, M, _} = Date,
+            {Y, M, calendar:last_day_of_the_month(Y, M)}
+    end.
\ No newline at end of file
diff --git a/src/ktn_fsm.erl b/src/ktn_fsm.erl
deleted file mode 100644
index f11ea62..0000000
--- a/src/ktn_fsm.erl
+++ /dev/null
@@ -1,67 +0,0 @@
-%%% @doc "Nicer" interfact to gen_fsm.
-%%% With this module you can:
-%%%   - use ktn_fsm:call instead of gen_fsm:sync_send_event
-%%%   - use ktn_fsm:call_through instead of gen_fsm:sync_send_all_state_event
-%%%   - use ktn_fsm:cast instead of gen_fsm:send_event
-%%%   - use ktn_fsm:cast_through instead of gen_fsm:sync_send_event
-%%%   - use ktn_fsm:state to retrieve the current gen_fsm state
-%%% @todo implement the inner functional changes:
-%%%   - leave some of the callbacks undefined. If a callback function is
-%%%     undefined, the caller will throw an exception, but the fsm will keep
-%%%     looping. This way you don't have to have catch-all clauses there.
--module(ktn_fsm).
-
--ignore_xref([start/3]).
-
--export(
-  [ state/1
-  , call/2
-  , call/3
-  , call_through/2
-  , call_through/3
-  , cast/2
-  , cast_through/2
-  ]).
-
--export(['$handle_undefined_function'/2]).
-
--spec state(sys:name()) -> atom().
-state(Fsm) ->
-  {StateName, _State} = sys:get_state(Fsm),
-  StateName.
-
--spec call(sys:name(), term()) -> term().
-call(Fsm, Call) ->
-  gen_fsm:sync_send_event(Fsm, Call).
-
--spec call(sys:name(), term(), timeout()) -> term().
-call(Fsm, Call, Timeout) ->
-  gen_fsm:sync_send_event(Fsm, Call, Timeout).
-
--spec call_through(sys:name(), term()) -> term().
-call_through(Fsm, Call) ->
-  gen_fsm:sync_send_all_state_event(Fsm, Call).
-
--spec call_through(sys:name(), term(), timeout()) -> term().
-call_through(Fsm, Call, Timeout) ->
-  gen_fsm:sync_send_all_state_event(Fsm, Call, Timeout).
-
--spec cast(sys:name(), term()) -> ok.
-cast(Fsm, Cast) ->
-  gen_fsm:send_event(Fsm, Cast).
-
--spec cast_through(sys:name(), term()) -> ok.
-cast_through(Fsm, Cast) ->
-  gen_fsm:send_all_state_event(Fsm, Cast).
-
--spec '$handle_undefined_function'(atom(), [any()]) -> any().
-'$handle_undefined_function'(Func, Args) ->
-  {module, gen_fsm} =
-    case code:is_loaded(gen_fsm) of
-      false -> code:load_file(gen_fsm);
-      _ -> {module, gen_fsm}
-    end,
-  case erlang:function_exported(gen_fsm, Func, length(Args)) of
-    true -> erlang:apply(gen_fsm, Func, Args);
-    false -> error_handler:raise_undef_exception(?MODULE, Func, Args)
-  end.
diff --git a/src/ktn_os.erl b/src/ktn_os.erl
index 777604a..c6e6618 100644
--- a/src/ktn_os.erl
+++ b/src/ktn_os.erl
@@ -3,7 +3,10 @@
 
 -export([command/1, command/2]).
 
--type opts() :: #{log_fun => fun((iodata()) -> any()), timeout => integer()}.
+-type opts() :: #{ log_fun => fun((iodata()) -> any())
+                 , timeout => integer()
+                 , monitor => reference()
+                 }.
 -type exit_status() :: integer().
 
 -spec command(iodata()) -> {exit_status(), string()}.
@@ -13,15 +16,19 @@ command(Cmd) ->
 
 -spec command(iodata(), opts()) -> {exit_status(), string()}.
 command(Cmd, Opts) ->
-  PortOpts = [stream, exit_status, eof],
+  PortOpts = [hide, stream, exit_status, eof, stderr_to_stdout],
   Port = open_port({spawn, shell_cmd()}, PortOpts),
-  erlang:port_command(Port, make_cmd(Cmd)),
-  get_data(Port, Opts, []).
+  MonRef = erlang:monitor(port, Port),
+  true = erlang:port_command(Port, make_cmd(Cmd)),
+  Result = get_data(Port, Opts#{monitor => MonRef}, []),
+  _ = demonitor(MonRef, [flush]),
+  Result.
 
 -spec get_data(port(), opts(), [string()]) -> {exit_status(), string()}.
 get_data(Port, Opts, Data) ->
   %% Get timeout option or an hour if undefined.
   Timeout = maps:get(timeout, Opts, 600000),
+  MonRef = maps:get(monitor, Opts),
   receive
     {Port, {data, NewData}} ->
       case maps:get(log_fun, Opts, undefined) of
@@ -30,13 +37,17 @@ get_data(Port, Opts, Data) ->
       end,
       get_data(Port, Opts, [NewData | Data]);
     {Port, eof} ->
-      port_close(Port),
+      catch port_close(Port),
+      flush_until_down(Port, MonRef),
       receive
         {Port, {exit_status, ExitStatus}} ->
           {ExitStatus, lists:flatten(lists:reverse(Data))}
-      end
+      end;
+    {'DOWN', MonRef, _, _, Reason} ->
+      flush_exit(Port),
+      exit({error, Reason, lists:flatten(lists:reverse(Data))})
   after
-    Timeout -> throw(timeout)
+    Timeout -> exit(timeout)
   end.
 
 -spec make_cmd(string()) -> iodata().
@@ -46,4 +57,27 @@ make_cmd(Cmd) ->
   [$(, unicode:characters_to_binary(Cmd), "\n) </dev/null; exit\n"].
 
 -spec shell_cmd() -> string().
-shell_cmd() -> "sh -s unix:cmd 2>&1".
+shell_cmd() -> "/bin/sh -s unix:cmd".
+
+%% When port_close returns we know that all the
+%% messages sent have been sent and that the
+%% DOWN message is after them all.
+flush_until_down(Port, MonRef) ->
+  receive
+    {Port, {data, Bytes}} ->
+      flush_until_down(Port, MonRef);
+    {'DOWN', MonRef, _, _, _} ->
+      flush_exit(Port)
+  end.
+
+%% The exit signal is always delivered before
+%% the down signal, so we can be sure that if there
+%% was an exit message sent, it will be in the
+%% mailbox now.
+flush_exit(Port) ->
+  receive
+    {'EXIT',  Port,  _} ->
+      ok
+  after 0 ->
+    ok
+  end.
diff --git a/test/cover.spec b/test/cover.spec
index 767df06..49614fa 100644
--- a/test/cover.spec
+++ b/test/cover.spec
@@ -4,7 +4,6 @@
   [ktn_binary,
    ktn_date,
    ktn_debug,
-   ktn_fsm,
    ktn_json,
    ktn_lists,
    ktn_maps,
diff --git a/test/ktn_date_SUITE.erl b/test/ktn_date_SUITE.erl
new file mode 100644
index 0000000..bf52405
--- /dev/null
+++ b/test/ktn_date_SUITE.erl
@@ -0,0 +1,50 @@
+-module(ktn_date_SUITE).
+
+-export([all/0]).
+
+-export([
+         shift_days/1,
+         shift_months/1
+        ]).
+
+-type config() :: [{atom(), term()}].
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Common test
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+-spec all() -> [atom()].
+all() ->
+    [
+     shift_days,
+     shift_months
+    ].
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Test Cases
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+-spec shift_days(config()) -> ok.
+shift_days(_Config) ->
+    Datetime = {{2018, 8, 15}, {12, 12, 12}},
+    {{2018, 8, 16}, {12, 12, 12}} = ktn_date:shift_days(Datetime, 1),
+    {{2018, 8, 14}, {12, 12, 12}} = ktn_date:shift_days(Datetime, -1),
+    {{2018, 9, 4},  {12, 12, 12}} = ktn_date:shift_days(Datetime, 20),
+    ok.
+
+-spec shift_months(config()) -> ok.
+shift_months(_Config) ->
+    {2018, 10, 15} = ktn_date:shift_months({2018, 8, 15}, 2),
+    {2018, 6,  15} = ktn_date:shift_months({2018, 8, 15}, -2),
+    {2019, 2,  15} = ktn_date:shift_months({2018, 8, 15}, 6),
+
+    {2018, 10, 31} = ktn_date:shift_months({2018, 8, 31}, 2),
+    {2018, 6,  30} = ktn_date:shift_months({2018, 8, 31}, -2),
+    {2019, 2,  28} = ktn_date:shift_months({2018, 8, 31}, 6),
+
+    ok = try
+        _ = ktn_date:shift_months({0, 8, 31}, -9),
+        out_of_bounds_expected
+    catch
+        error:out_of_bounds -> ok
+    end.
\ No newline at end of file
diff --git a/test/ktn_fsm_SUITE.erl b/test/ktn_fsm_SUITE.erl
deleted file mode 100644
index 59e4b37..0000000
--- a/test/ktn_fsm_SUITE.erl
+++ /dev/null
@@ -1,109 +0,0 @@
--module(ktn_fsm_SUITE).
-
--export([all/0]).
-
--export([ secure_vault/1
-        ]).
-
--type config() :: [{atom(), term()}].
-
-all() -> [secure_vault].
-
--spec secure_vault(config()) -> {comment, []}.
-secure_vault(_Config) ->
-  ct:comment("We start the vault with a master password and 3 attempts limit"),
-  {ok, V} = secure_vault:start(masterpwd, 3),
-
-  ct:comment("While it's unlocked, we can push, pop and check contents"),
-  unlocked = secure_vault:state(V),
-  [] = secure_vault:contents(V),
-  0 = secure_vault:count(V),
-  ok = secure_vault:push(V, a),
-  ok = secure_vault:push(V, b),
-  [b, a] = secure_vault:contents(V),
-  2 = secure_vault:count(V),
-  b = secure_vault:pop(V),
-  [a] = secure_vault:contents(V),
-  1 = secure_vault:count(V),
-  V ! ignored,
-
-  ct:comment("We lock it with password 'pwd'"),
-  ok = secure_vault:lock(V, pwd),
-  locked = secure_vault:state(V),
-  V ! ignored,
-
-  ct:comment("While it's locked we can't do anything besides count & unlock"),
-  1 = secure_vault:count(V),
-  badstate = fails(fun() -> secure_vault:lock(V, pwd2) end),
-  badstate = fails(fun() -> secure_vault:lock(V, pwd) end),
-  badstate = fails(fun() -> secure_vault:pop(V) end),
-  ok = secure_vault:push(V, notpushed),
-  1 = secure_vault:count(V),
-  badstate = fails(fun() -> secure_vault:contents(V) end),
-  V ! ignored,
-
-  ct:comment("We try to unlock it with a wrong password"),
-  badpwd = fails(fun() -> secure_vault:unlock(V, notpwd) end),
-  locked = secure_vault:state(V),
-  V ! ignored,
-
-  ct:comment("We unlock it with the right password"),
-  ok = secure_vault:unlock(V, pwd),
-  V ! ignored,
-
-  ct:comment("While it's unlocked, we can push, pop and check contents again"),
-  [a] = secure_vault:contents(V),
-  1 = secure_vault:count(V),
-  a = secure_vault:pop(V),
-  [] = secure_vault:contents(V),
-  0 = secure_vault:count(V),
-  ok = secure_vault:push(V, x),
-  V ! ignored,
-
-  ct:comment("While it's unlocked, we can't unlock it again"),
-  badstate = fails(fun() -> secure_vault:unlock(V, pwd2) end),
-  badstate = fails(fun() -> secure_vault:unlock(V, pwd) end),
-
-  ct:comment("We lock it again"),
-  ok = secure_vault:lock(V, pwd3),
-
-  ct:comment("After 3 invalid pwd attempts, the vault is closed"),
-  badpwd = fails(fun() -> secure_vault:unlock(V, notpwd3) end),
-  badpwd = fails(fun() -> secure_vault:unlock(V, notpwd3) end),
-  closed = fails(fun() -> secure_vault:unlock(V, notpwd3) end),
-  closed = secure_vault:state(V),
-
-  ct:comment("When a vault is closed, there is nothing we can do..."),
-  badstate = fails(fun() -> secure_vault:unlock(V, pwd3) end),
-  badstate = fails(fun() -> secure_vault:lock(V, pwd3) end),
-  badstate = fails(fun() -> secure_vault:lock(V, pwd) end),
-  badstate = fails(fun() -> secure_vault:pop(V) end),
-  ok = secure_vault:push(V, notpushed),
-  1 = secure_vault:count(V),
-  badstate = fails(fun() -> secure_vault:contents(V) end),
-
-  ct:comment("...except stopping it with the master password"),
-  1 = secure_vault:count(V),
-  ok = secure_vault:stop(V, notmasterpwd),
-  1 = secure_vault:count(V),
-  badstate = fails(fun() -> secure_vault:contents(V) end),
-  V ! ignored,
-
-  ok = secure_vault:stop(V, masterpwd),
-  noproc =
-    ktn_task:wait_for(
-      fun() ->
-        fails(fun() -> secure_vault:contents(V) end)
-      end, noproc, 500, 10),
-
-  {comment, ""}.
-
-fails(Fun) ->
-  try Fun() of
-    Response ->
-      ct:fail("Unexpected response from ~p: ~p", [Fun, Response])
-  catch
-    _:{noproc, _} -> noproc;
-    _:Error -> Error
-  end.
-
diff --git a/test/ktn_os_SUITE.erl b/test/ktn_os_SUITE.erl
index 4b69ef1..eb8013a 100644
--- a/test/ktn_os_SUITE.erl
+++ b/test/ktn_os_SUITE.erl
@@ -68,9 +68,45 @@ command(_Config) ->
   YesFun = fun() ->
                case ktn_os:command("yes > /dev/null") of
                  {ExitStatus, _} when ExitStatus =/= 0 -> Self ! ok;
-                 _ -> Self ! error
+                 UnexpectedResult -> Self ! {error, UnexpectedResult}
                end
            end,
   erlang:spawn_link(YesFun),
   [] = os:cmd("pkill yes"),
-  ok  = receive X -> X after 1000 -> error end.
+  ok  = receive X -> X after 1000 -> timeout end,
+
+  ct:comment("Check result when port is closed"),
+  Yes2Fun =
+    fun() ->
+      process_flag(trap_exit, true),
+      try ktn_os:command("yes > /dev/null") of
+        UnexpectedResult -> Self ! {error, UnexpectedResult}
+      catch
+        exit:{error, _, _} -> Self ! ok
+      end
+    end,
+  FindPort =
+    fun(Proc) ->
+      fun() ->
+        {links, Links} = erlang:process_info(Proc, links),
+        [_] = [P || P <- Links, is_port(P)]
+      end
+    end,
+  Pid = erlang:spawn(Yes2Fun),
+  try
+    [Port] = ktn_task:wait_for_success(FindPort(Pid)),
+    port_close(Port),
+    ok  = receive X2 -> X2 after 1000 -> timeout end
+  after
+    exit(Pid, kill)
+  end,
+
+  ct:comment("Check result when port is killed"),
+  Pid2 = erlang:spawn(Yes2Fun),
+  try
+    [Port2] = ktn_task:wait_for_success(FindPort(Pid2)),
+    exit(Port2, kill),
+    ok  = receive X3 -> X3 after 1000 -> timeout end
+  after
+    exit(Pid2, kill)
+  end.
diff --git a/test/secure_vault.erl b/test/secure_vault.erl
deleted file mode 100644
index bc368fa..0000000
--- a/test/secure_vault.erl
+++ /dev/null
@@ -1,136 +0,0 @@
--module(secure_vault).
-
--behaviour(gen_fsm).
-
--ignore_xref([{ktn_fsm, start, 3}]).
-
--export([ start/2
-        , state/1
-        , contents/1
-        , count/1
-        , push/2
-        , pop/1
-        , lock/2
-        , unlock/2
-        , stop/2
-        ]).
-
--export([ init/1
-        , handle_event/3
-        , handle_sync_event/4
-        , handle_info/3
-        , terminate/3
-        , code_change/4
-        ]).
-
--export([ unlocked/3
-        , unlocked/2
-        , locked/3
-        , locked/2
-        , closed/3
-        , closed/2
-        ]).
-
--dialyzer({no_missing_calls, [start/2]}).
-
-start(MasterPassword, MaxAttempts) ->
-  ktn_fsm:start(
-    ?MODULE,
-    {MasterPassword, MaxAttempts},
-    [{debug, [log, statistics, trace]}]).
-
-state(Vault) ->
-  ktn_fsm:state(Vault).
-
-contents(Vault) ->
-  call(Vault, contents).
-
-count(Vault) ->
-  call(Vault, count).
-
-push(Vault, Content) ->
-  ktn_fsm:cast(Vault, {push, Content}).
-
-pop(Vault) ->
-  call(Vault, pop).
-
-lock(Vault, Password) ->
-  call(Vault, {lock, Password}).
-
-unlock(Vault, Password) ->
-  call(Vault, {unlock, Password}).
-
-stop(Vault, Password) ->
-  ktn_fsm:cast_through(Vault, {stop, Password}).
-
-init({MasterPassword, Attempts}) ->
-  StateData =
-    #{ masterpwd    => MasterPassword
-     , max_attempts => Attempts
-     , contents     => []
-     },
-  {ok, unlocked, StateData}.
-
-unlocked({lock, Password}, _From, StateData) ->
-  #{max_attempts := Attempts} = StateData,
-  {reply, ok, locked, StateData#{password => Password, attempts => Attempts}};
-unlocked(pop, _From, #{contents := []} = StateData) ->
-  {reply, {ok, undefined}, unlocked, StateData};
-unlocked(pop, _From, StateData) ->
-  #{contents := [Content|Contents]} = StateData,
-  {reply, {ok, Content}, unlocked, StateData#{contents := Contents}};
-unlocked(contents, _From, StateData) ->
-  #{contents := Contents} = StateData,
-  {reply, {ok, Contents}, unlocked, StateData};
-unlocked(_, _From, StateData) ->
-  {reply, {error, badstate}, unlocked, StateData}.
-
-unlocked({push, Content}, StateData) ->
-  #{contents := Contents} = StateData,
-  {next_state, unlocked, StateData#{contents := [Content|Contents]}}.
-
-locked({unlock, Password}, _From, #{password := Password} = StateData) ->
-  {reply, ok, unlocked, maps:remove(password, StateData)};
-locked({unlock, _NotPassword}, _From, #{attempts := 1} = StateData) ->
-  {reply, {error, closed}, closed, StateData};
-locked({unlock, _NotPassword}, _From, StateData) ->
-  #{attempts := Attempts} = StateData,
-  {reply, {error, badpwd}, locked, StateData#{attempts := Attempts - 1}};
-locked(_, _From, StateData) ->
-  {reply, {error, badstate}, locked, StateData}.
-
-locked(_, StateData) ->
-  {next_state, locked, StateData}.
-
-closed(_, _From, StateData) ->
-  {reply, {error, badstate}, closed, StateData}.
-
-closed(_, StateData) ->
-  {next_state, closed, StateData}.
-
-handle_sync_event(count, _From, StateName, StateData) ->
-  #{contents := Contents} = StateData,
-  {reply, {ok, length(Contents)}, StateName, StateData}.
-
-handle_event({stop, Pwd}, _StateName, #{masterpwd := Pwd} = StateData) ->
-  {stop, normal, StateData};
-handle_event(_, StateName, StateData) ->
-  {next_state, StateName, StateData}.
-
-handle_info(_, StateName, StateData) ->
-  {next_state, StateName, StateData}.
-
-terminate(_Reason, _StateName, _StateData) -> ok.
-
-code_change(_OldVsn, StateName, StateData, _Extra) ->
-  {ok, StateName, StateData}.
-
-call(Vault, count) -> call(Vault, count, fun ktn_fsm:call_through/2);
-call(Vault, Call) -> call(Vault, Call, fun ktn_fsm:call/2).
-
-call(Vault, Call, Fun) ->
-  case Fun(Vault, Call) of
-    ok -> ok;
-    {ok, Result} -> Result;
-    {error, Error} -> throw(Error)
-  end.
openSUSE Build Service is sponsored by