Skip to content
Permalink
Browse files

Merge pull request #1113 from FarmBot/uart_tests

UART Updates
  • Loading branch information
RickCarlino committed Jan 14, 2020
2 parents c5c8bb4 + a61c69c commit 2d17b4e82f2e6dad1eaa7e4d97f4f7b0a4bd67fb
@@ -0,0 +1,5 @@
use Mix.Config

if Mix.env() == :test do
config :farmbot_firmware, :uart_adapter, FarmbotFirmware.UartTestAdapter
end
@@ -0,0 +1,17 @@
defmodule FarmbotFirmware.UartAdapter do
alias Circuits.UART

@type uart_pid :: GenServer.server()
@type device_path :: binary()
@type gcode :: iodata()
@type not_known :: any()
@type uart_opts :: [UART.uart_option()]
@type uart_op_result :: :ok | {:error, term()}
@type uart_start_link_result :: {:ok, pid()} | {:error, term()}

@callback generate_opts() :: not_known
@callback open(uart_pid, device_path, uart_opts) :: uart_op_result
@callback start_link() :: uart_start_link_result
@callback stop(uart_pid) :: :ok
@callback write(uart_pid, gcode) :: uart_op_result
end
@@ -0,0 +1,37 @@
defmodule FarmbotFirmware.UartDefaultAdapter do
@moduledoc """
A thin wrapper of Circuits.UART to simplify testing.
"""
alias Circuits.UART
@behaviour FarmbotFirmware.UartAdapter

@impl FarmbotFirmware.UartAdapter
def start_link do
UART.start_link()
end

@impl FarmbotFirmware.UartAdapter
def open(uart_pid, device_path, opts) do
UART.open(uart_pid, device_path, opts)
end

@impl FarmbotFirmware.UartAdapter
def stop(uart_pid) do
IO.puts("Hello?")
UART.stop(uart_pid)
end

@impl FarmbotFirmware.UartAdapter
def write(uart_pid, str) do
UART.write(uart_pid, str)
end

@impl FarmbotFirmware.UartAdapter
def generate_opts do
[
active: true,
speed: 115_200,
framing: {UART.Framing.Line, separator: "\r\n"}
]
end
end
@@ -4,8 +4,7 @@ defmodule FarmbotFirmware.UARTTransport do
This is the mechanism that official Farmbot's communicate with
official Farmbot-Arduino-Firmware's over.
"""
alias FarmbotFirmware.GCODE
alias Circuits.UART
alias FarmbotFirmware.{GCODE, UartDefaultAdapter}
use GenServer
require Logger

@@ -15,16 +14,16 @@ defmodule FarmbotFirmware.UARTTransport do
device = Keyword.fetch!(args, :device)
handle_gcode = Keyword.fetch!(args, :handle_gcode)
reset = Keyword.get(args, :reset)
{:ok, uart} = UART.start_link()
{:ok, uart} = uart_adapter().start_link()
{:ok, %{uart: uart, device: device, open: false, handle_gcode: handle_gcode, reset: reset}, 0}
end

def terminate(_, state) do
UART.stop(state.uart)
def terminate(_, %{uart: uart}) do
uart_adapter().stop(uart)
end

def handle_info(:timeout, %{open: false} = state) do
opts = [active: true, speed: 115_200, framing: {UART.Framing.Line, separator: "\r\n"}]
opts = uart_adapter().generate_opts()

with :ok <- open(state.uart, state.device, opts),
:ok <- reset(state) do
@@ -48,7 +47,7 @@ defmodule FarmbotFirmware.UARTTransport do

def handle_call(code, _from, state) do
str = GCODE.encode(code)
r = UART.write(state.uart, str)
r = uart_adapter().write(state.uart, str)
{:reply, r, state}
end

@@ -61,6 +60,10 @@ defmodule FarmbotFirmware.UARTTransport do
end

def open(uart_pid, device_path, opts) do
UART.open(uart_pid, device_path, opts)
uart_adapter().open(uart_pid, device_path, opts)
end

def uart_adapter() do
Application.get_env(:farmbot_firmware, :uart_adapter, UartDefaultAdapter)
end
end
@@ -52,6 +52,7 @@ defmodule FarmbotFirmware.MixProject do
{:circuits_uart, "~> 1.4.0"},
{:excoveralls, "~> 0.10", only: [:test], targets: [:host]},
{:dialyxir, "~> 1.0.0-rc.3", only: [:dev], targets: [:host], runtime: false},
{:mox, "~> 0.5.1", only: :test},
{:ex_doc, "~> 0.21.2", only: [:dev], targets: [:host], runtime: false}
]
end
@@ -25,6 +25,7 @@
"metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm"},
"mime": {:hex, :mime, "1.3.0", "5e8d45a39e95c650900d03f897fbf99ae04f60ab1daa4a34c7a20a5151b7a5fe", [:mix], [], "hexpm"},
"mimerl": {:hex, :mimerl, "1.0.2", "993f9b0e084083405ed8252b99460c4f0563e41729ab42d9074fd5e52439be88", [:rebar3], [], "hexpm"},
"mox": {:hex, :mox, "0.5.1", "f86bb36026aac1e6f924a4b6d024b05e9adbed5c63e8daa069bd66fb3292165b", [:mix], [], "hexpm"},
"nerves_uart": {:hex, :nerves_uart, "1.2.0", "195424116b925cd3bf9d666be036c2a80655e6ca0f8d447e277667a60005c50e", [:mix], [{:elixir_make, "~> 0.4", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm"},
"nimble_parsec": {:hex, :nimble_parsec, "0.5.3", "def21c10a9ed70ce22754fdeea0810dafd53c2db3219a0cd54cf5526377af1c6", [:mix], [], "hexpm"},
"parse_trans": {:hex, :parse_trans, "3.3.0", "09765507a3c7590a784615cfd421d101aec25098d50b89d7aa1d66646bc571c1", [:rebar3], [], "hexpm"},
@@ -0,0 +1,93 @@
Mox.defmock(FarmbotFirmware.UartTestAdapter, for: FarmbotFirmware.UartAdapter)

defmodule FarmbotFirmware.UARTTransportTest do
use ExUnit.Case
import Mox
setup [:verify_on_exit!]
doctest FarmbotFirmware.UARTTransport
alias FarmbotFirmware.{UartTestAdapter, UARTTransport}

test "UARTTransport.init/1" do
expect(UartTestAdapter, :start_link, fn ->
{:ok, :FAKE_UART}
end)

init_args = [
device: :FAKE_DEVICE,
handle_gcode: :FAKE_GCODE_HANDLER,
reset: :FAKE_RESETER
]

{:ok, state, 0} = UARTTransport.init(init_args)
assert state.device == Keyword.fetch!(init_args, :device)
assert state.handle_gcode == Keyword.fetch!(init_args, :handle_gcode)
assert state.reset == Keyword.fetch!(init_args, :reset)
end

test "UARTTransport.terminate/2" do
expect(UartTestAdapter, :stop, fn uart ->
assert uart == :whatever
end)

state = %{uart: :whatever}
UARTTransport.terminate(nil, state)
end

test "UARTTransport resets UART on timeout" do
state = %{uart: :FAKE_UART, device: :FAKE_DEVICE, open: false}

fake_opts = [fake_opts: true]

expect(UartTestAdapter, :generate_opts, fn ->
fake_opts
end)

expect(UartTestAdapter, :open, fn uart, dev, opts ->
assert uart == state.uart
assert dev == state.device
assert fake_opts == opts
:ok
end)

{:noreply, state2} = UARTTransport.handle_info(:timeout, state)
# Expect the `open` state to toggle back to "true" from "false":
refute state.open == state2.open
end

test "UARTTransport handles unexpected UART errors" do
state = %{uart: :FAKE_UART, device: :FAKE_DEVICE, open: false}

fake_opts = [fake_opts: true]

expect(UartTestAdapter, :generate_opts, fn ->
fake_opts
end)

expect(UartTestAdapter, :open, fn _, _, _ ->
{:error, "Simulated UART failure. This is OK"}
end)

{:noreply, state2, retry_timeout} = UARTTransport.handle_info(:timeout, state)
assert retry_timeout == 5000
assert state.open == state2.open
end

test "UARTTransport.reset/2" do
empty_state = %{reset: nil}
ok_state = %{reset: %{reset: :fake_reset}}
assert :ok == UARTTransport.reset(empty_state)
assert :fake_reset == UARTTransport.reset(ok_state)
end

test "UARTTransport.open/2" do
me = self()

expect(UartTestAdapter, :open, fn pid, path, opts ->
assert pid == me
assert path == "/dev/null"
assert opts == [a: :b]
end)

UARTTransport.open(me, "/dev/null", a: :b)
end
end
@@ -1 +1,2 @@
Application.ensure_all_started(:mox)
ExUnit.start()

0 comments on commit 2d17b4e

Please sign in to comment.
You can’t perform that action at this time.