-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Generate code required to mock Erlang modules
- Loading branch information
1 parent
839c28c
commit ef90bcb
Showing
4 changed files
with
75 additions
and
78 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
defmodule Gen do | ||
@moduledoc false | ||
|
||
@doc """ | ||
Generates a behaviour based on an existing module. | ||
Mox requires a behaviour to be defined in order to create a mock. To mock | ||
core modules in tests - e.g. :ssh, :ssh_connection and :ssh_sftp - we need | ||
behaviours mirroring their public API. | ||
""" | ||
def defbehaviour(name, target) when is_atom(name) and is_atom(target) do | ||
info = moduledoc("Generated behaviour for #{inspect(target)}.") | ||
|
||
body = | ||
for {fun, arity} <- functions(target) do | ||
args = 0..arity |> Enum.map(fn _ -> {:term, [], []} end) |> tl() | ||
|
||
quote do | ||
@callback unquote(fun)(unquote_splicing(args)) :: term() | ||
end | ||
end | ||
|
||
Module.create(name, info ++ body, Macro.Env.location(__ENV__)) | ||
end | ||
|
||
@doc """ | ||
Generates a module delegating all function calls to another module. | ||
Mox requires modules used for stubbing to implement the mocked behaviour. To | ||
mock core modules without behaviour definitions, we generate stand-in modules | ||
which delegate | ||
""" | ||
def defdelegated(name, target, options \\ []) | ||
when is_atom(name) and is_atom(target) and is_list(options) do | ||
info = | ||
moduledoc("Generated stand-in module for #{inspect(target)}.") ++ | ||
behaviour(Keyword.get(options, :behaviour)) | ||
|
||
body = | ||
for {fun, arity} <- functions(target) do | ||
args = Macro.generate_arguments(arity, name) | ||
|
||
quote do | ||
defdelegate unquote(fun)(unquote_splicing(args)), to: unquote(target) | ||
end | ||
end | ||
|
||
Module.create(name, info ++ body, Macro.Env.location(__ENV__)) | ||
end | ||
|
||
defp functions(module) do | ||
exports = module.module_info(:exports) | ||
Keyword.drop(exports, ~w[__info__ module_info]a) | ||
end | ||
|
||
defp moduledoc(nil), do: [] | ||
defp moduledoc(docstr), do: [quote(do: @moduledoc(unquote(docstr)))] | ||
|
||
defp behaviour(nil), do: [] | ||
defp behaviour(name), do: [quote(do: @behaviour(unquote(name)))] | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,79 +1,13 @@ | ||
defmodule ErlangSshBehaviour do | ||
@moduledoc false | ||
require Gen | ||
|
||
@type conn() :: term() | ||
Gen.defbehaviour(ErlangSsh.Behaviour, :ssh) | ||
Gen.defdelegated(ErlangSsh, :ssh, behaviour: ErlangSsh.Behaviour) | ||
Mox.defmock(MockErlangSsh, for: ErlangSsh.Behaviour) | ||
|
||
@callback connect(binary(), integer(), keyword(), timeout()) :: {:ok, conn()} | {:error, term()} | ||
@callback close(conn()) :: :ok | ||
end | ||
Gen.defbehaviour(ErlangSshConnection.Behaviour, :ssh_connection) | ||
Gen.defdelegated(ErlangSshConnection, :ssh_connection, behaviour: ErlangSshConnection.Behaviour) | ||
Mox.defmock(MockErlangSshConnection, for: ErlangSshConnection.Behaviour) | ||
|
||
defmodule ErlangSsh do | ||
@moduledoc false | ||
|
||
@behaviour ErlangSshBehaviour | ||
|
||
defdelegate connect(host, port, options, timeout), to: :ssh | ||
defdelegate close(conn), to: :ssh | ||
end | ||
|
||
Mox.defmock(MockErlangSsh, for: ErlangSshBehaviour) | ||
|
||
defmodule ErlangSshConnectionBehaviour do | ||
@moduledoc false | ||
|
||
@type conn() :: term() | ||
@type chan() :: integer() | ||
|
||
@callback session_channel(conn(), integer(), integer(), timeout()) :: | ||
{:ok, chan()} | {:error, term()} | ||
@callback subsystem(conn(), chan(), charlist(), timeout()) :: | ||
:success | :failure | {:error, :timeout} | {:error, :closed} | ||
@callback close(conn(), chan()) :: :ok | ||
@callback exec(conn(), chan(), binary(), timeout()) :: | ||
:success | :failure | {:error, :timeout} | {:error, :closed} | ||
@callback ptty_alloc(conn(), chan(), keyword(), timeout()) :: | ||
:success | :failure | {:error, :timeout} | {:error, :closed} | ||
@callback send(conn(), chan(), 0..1, binary(), timeout()) :: | ||
:ok | {:error, :timeout} | {:error, :closed} | ||
@callback send_eof(conn(), chan()) :: :ok | {:error, :closed} | ||
@callback adjust_window(conn(), chan(), integer()) :: :ok | ||
end | ||
|
||
defmodule ErlangSshConnection do | ||
@moduledoc false | ||
|
||
@behaviour ErlangSshConnectionBehaviour | ||
|
||
defdelegate session_channel(conn, initial_window_size, max_packet_size, timeout), | ||
to: :ssh_connection | ||
|
||
defdelegate subsystem(conn, chan, name, timeout), to: :ssh_connection | ||
defdelegate close(conn, chan), to: :ssh_connection | ||
defdelegate exec(conn, chan, command, timeout), to: :ssh_connection | ||
defdelegate ptty_alloc(conn, chan, keyword, timeout), to: :ssh_connection | ||
defdelegate send(conn, chan, type, data, timeout), to: :ssh_connection | ||
defdelegate send_eof(conn, chan), to: :ssh_connection | ||
defdelegate adjust_window(conn, chan, size), to: :ssh_connection | ||
end | ||
|
||
Mox.defmock(MockErlangSshConnection, for: ErlangSshConnectionBehaviour) | ||
|
||
defmodule ErlangSshSftpBehaviour do | ||
@moduledoc false | ||
|
||
@type conn() :: term() | ||
@type chan() :: pid() | ||
|
||
# TODO | ||
@callback start_channel(conn(), keyword()) :: {:ok, chan()} | {:error, term()} | ||
end | ||
|
||
defmodule ErlangSshSftp do | ||
@moduledoc false | ||
|
||
@behaviour ErlangSshSftpBehaviour | ||
|
||
defdelegate start_channel(conn, options), to: :ssh_sftp | ||
end | ||
|
||
Mox.defmock(MockErlangSshSftp, for: ErlangSshSftpBehaviour) | ||
Gen.defbehaviour(ErlangSshSftp.Behaviour, :ssh_sftp) | ||
Gen.defdelegated(ErlangSshSftp, :ssh_sftp, behaviour: ErlangSshSftp.Behaviour) | ||
Mox.defmock(MockErlangSshSftp, for: ErlangSshSftp.Behaviour) |