we have a real coverage tool now. Write some tests

pull/208/head
connor rigby 2016-11-29 15:11:25 -08:00
parent e32cdf7274
commit 7c8cfa7960
22 changed files with 360 additions and 19 deletions

1
.gitignore vendored
View File

@ -11,3 +11,4 @@
erl_crash.dump
npm-debug.log
ttb_last_config
cover

6
coveralls.json 100644
View File

@ -0,0 +1,6 @@
{
"custom_stop_words": [
"ntpd",
"check_time_set"
]
}

View File

@ -55,6 +55,10 @@ defmodule Farmbot.BotState.Authorization do
dispatch state.server, state
end
def handle_call(:get_token, _from, %State{} = state) do
dispatch state.token, state
end
def handle_call(event, _from, %State{} = state) do
Logger.warn("[#{__MODULE__}] UNHANDLED CALL!: #{inspect event}", [__MODULE__])
dispatch :unhandled, state
@ -77,6 +81,13 @@ defmodule Farmbot.BotState.Authorization do
dispatch state
end
# this is pretty much only for testing.
def handle_info({:authorization, %Token{} = token}, %State{} = state) do
new_state =
%State{state | token: token, server: token.unencoded.iss}
dispatch new_state
end
defp dispatch(reply, %State{} = state) do
State.broadcast(state)
{:reply, reply, state}

View File

@ -57,7 +57,11 @@ defmodule Farmbot.BotState.Configuration do
environment: env,
throttled: get_throttled
}}
{:ok, State.broadcast(
state = load(initial_state)
{:ok, State.broadcast(state)}
end
def load(initial_state) do
case SafeStorage.read(__MODULE__) do
{:ok, %State{} = last_state} ->
Logger.debug("Loading previous Bot Configuration State")
@ -66,7 +70,7 @@ defmodule Farmbot.BotState.Configuration do
# Maybe persiste locks?
_ ->
initial_state
end)}
end
end
def start_link(args) do
@ -154,12 +158,12 @@ defmodule Farmbot.BotState.Configuration do
# Lock the frontend from doing stuff
def handle_cast({:add_lock, string}, %State{} = state) do
maybe_index = Enum.find_index(state.locks, fn(%{reason: str}) -> str == string end)
# check if this lock already exists.
cond do
# this lock already exists. don't do anything.
is_integer(maybe_index) ->
new_state = %State{state | locks: List.replace_at(state.locks,
maybe_index,
%{reason: string})}
dispatch new_state
dispatch state
# This lock does not exist. (the check is nil). add a new lock.
is_nil(maybe_index) ->
new_state = %State{locks: state.locks ++ [%{reason: string}]}
dispatch new_state

View File

@ -8,10 +8,7 @@ defmodule Ast do
kind: Strint.t
}
@enforce_keys [:args, :body, :kind]
defstruct [:args, :body, :kind]
# %{"args" => %{"message" => "hello world"},
# "body" =>
# [%{"args" => %{"channel_name" => "toast_error"}, "kind" => "channel"}], "kind" => "send_message"}
defstruct @enforce_keys
@spec parse(map) :: t
def parse(%{"kind" => kind, "args" => args, "body" => body}) do

View File

@ -10,7 +10,7 @@ defmodule Corpus do
args: list(any),
nodes: list(any)}
@spec create(map) :: t
@spec create(map) :: t | :error
def create(%{
"tag" => tag,
"args" => args,
@ -20,4 +20,5 @@ defmodule Corpus do
args: args,
nodes: nodes}
end
def create(_), do: :error
end

View File

@ -30,4 +30,5 @@ defmodule Device do
name: name,
webcam_url: wcu}
end
def create(_), do: :error
end

View File

@ -37,4 +37,5 @@ defmodule Peripheral do
created_at: created_at,
updated_at: updated_at}
end
def create(_), do: :error
end

View File

@ -5,7 +5,9 @@ defmodule Plant do
defstruct []
@type t :: %__MODULE__{}
@spec create(map) :: t
def create(_map) do
def create(map)
when is_map(map) do
%Plant{}
end
def create(_), do: :error
end

View File

@ -25,4 +25,5 @@ defmodule Regimen do
color: color,
name: name}
end
def create(_), do: :error
end

View File

@ -29,4 +29,5 @@ defmodule RegimenItem do
regimen_id: regimen_id,
sequence_id: sequence_id}
end
def create(_), do: :error
end

View File

@ -33,4 +33,5 @@ defmodule Sequence do
kind: "sequence",
name: name}
end
def create(_), do: :error
end

View File

@ -82,5 +82,5 @@ defmodule Token do
os_update_server: os_update_server
}}
end
def create(_), do: :not_valid
def create(_), do: :error
end

View File

@ -40,4 +40,5 @@ defmodule User do
created_at: created_at,
updated_at: updated_at}
end
def create(_), do: :error
end

View File

@ -20,6 +20,7 @@ defmodule Farmbot.Mixfile do
def project do
[app: :farmbot,
test_coverage: [tool: ExCoveralls],
version: @version,
target: target(Mix.env),
archives: [nerves_bootstrap: "~> 0.1.4"],
@ -110,7 +111,8 @@ defmodule Farmbot.Mixfile do
deps ++ deps(:dev) ++
[ {:plug, "~> 1.0"},
{:cors_plug, "~> 1.1"},
{:cowboy, "~> 1.0.0"} ]
{:cowboy, "~> 1.0.0"},
{:excoveralls, "~> 0.5"} ]
end
def deps(:dev) do

View File

@ -4,6 +4,7 @@
"cf": {:hex, :cf, "0.2.1", "69d0b1349fd4d7d4dc55b7f407d29d7a840bf9a1ef5af529f1ebe0ce153fc2ab", [:rebar3], []},
"combine": {:hex, :combine, "0.9.2", "cd3c8721f378ebe032487d8a4fa2ced3181a456a3c21b16464da8c46904bb552", [:mix], []},
"cors_plug": {:hex, :cors_plug, "1.1.2", "3e7451286996f745c7b629c39d24a6493e59b0c8191f27e67f6ab097f96ffd23", [:mix], [{:cowboy, "~> 1.0.0", [hex: :cowboy, optional: false]}, {:plug, "> 0.8.0", [hex: :plug, optional: false]}]},
"coverex": {:hex, :coverex, "1.4.10", "f6b68f95b3d51d04571a09dd2071c980e8398a38cf663db22b903ecad1083d51", [:mix], [{:httpoison, "~> 0.9", [hex: :httpoison, optional: false]}, {:poison, "~> 1.5 or ~> 2.0", [hex: :poison, optional: false]}]},
"cowboy": {:hex, :cowboy, "1.0.4", "a324a8df9f2316c833a470d918aaf73ae894278b8aa6226ce7a9bf699388f878", [:make, :rebar], [{:cowlib, "~> 1.0.0", [hex: :cowlib, optional: false]}, {:ranch, "~> 1.0", [hex: :ranch, optional: false]}]},
"cowlib": {:hex, :cowlib, "1.0.2", "9d769a1d062c9c3ac753096f868ca121e2730b9a377de23dec0f7e08b1df84ee", [:make], []},
"credo": {:hex, :credo, "0.5.2", "92e8c9f86e0ffbf9f688595e9f4e936bc96a52e5606d2c19713e9e4d191d5c74", [:mix], [{:bunt, "~> 0.1.6", [hex: :bunt, optional: false]}]},
@ -11,6 +12,7 @@
"elixir_ale": {:hex, :elixir_ale, "0.5.6", "a94c89a17ec39ad4c4a9a90d4f00bd9f33b2f2e98703317fbdf5efceab276b44", [:make, :mix], [{:elixir_make, "~> 0.3", [hex: :elixir_make, optional: false]}]},
"elixir_make": {:hex, :elixir_make, "0.4.0", "992f38fabe705bb45821a728f20914c554b276838433349d4f2341f7a687cddf", [:mix], []},
"erlware_commons": {:hex, :erlware_commons, "0.21.0", "a04433071ad7d112edefc75ac77719dd3e6753e697ac09428fc83d7564b80b15", [:rebar3], [{:cf, "0.2.1", [hex: :cf, optional: false]}]},
"excoveralls": {:hex, :excoveralls, "0.5.7", "5d26e4a7cdf08294217594a1b0643636accc2ad30e984d62f1d166f70629ff50", [:mix], [{:exjsx, "~> 3.0", [hex: :exjsx, optional: false]}, {:hackney, ">= 0.12.0", [hex: :hackney, optional: false]}]},
"exjsx": {:hex, :exjsx, "3.2.1", "1bc5bf1e4fd249104178f0885030bcd75a4526f4d2a1e976f4b428d347614f0f", [:mix], [{:jsx, "~> 2.8.0", [hex: :jsx, optional: false]}]},
"exrm": {:hex, :exrm, "1.0.8", "5aa8990cdfe300282828b02cefdc339e235f7916388ce99f9a1f926a9271a45d", [:mix], [{:relx, "~> 3.5", [hex: :relx, optional: false]}]},
"fake_nerves": {:git, "https://github.com/ConnorRigby/fake_nerves.git", "fad032a71e624ebeb06ecb5089c61583f8e111e0", []},
@ -21,6 +23,7 @@
"getopt": {:hex, :getopt, "0.8.2", "b17556db683000ba50370b16c0619df1337e7af7ecbf7d64fbf8d1d6bce3109b", [:rebar], []},
"gettext": {:hex, :gettext, "0.12.1", "c0624f52763469ef7a3674919ae28b8286d88195b90fa1516180f31bbbd26d14", [:mix], []},
"hackney": {:hex, :hackney, "1.6.3", "d489d7ca2d4323e307bedc4bfe684323a7bf773ecfd77938f3ee8074e488e140", [:mix, :rebar3], [{:certifi, "0.7.0", [hex: :certifi, optional: false]}, {:idna, "1.2.0", [hex: :idna, optional: false]}, {:metrics, "1.0.1", [hex: :metrics, optional: false]}, {:mimerl, "1.0.2", [hex: :mimerl, optional: false]}, {:ssl_verify_fun, "1.1.1", [hex: :ssl_verify_fun, optional: false]}]},
"httpoison": {:hex, :httpoison, "0.10.0", "4727b3a5e57e9a4ff168a3c2883e20f1208103a41bccc4754f15a9366f49b676", [:mix], [{:hackney, "~> 1.6.3", [hex: :hackney, optional: false]}]},
"httpotion": {:hex, :httpotion, "3.0.2", "525b9bfeb592c914a61a8ee31fdde3871e1861dfe805f8ee5f711f9f11a93483", [:mix], [{:ibrowse, "~> 4.2", [hex: :ibrowse, optional: false]}]},
"hulaaki": {:git, "https://github.com/ConnorRigby/hulaaki.git", "47b77e8673ce522f9475e0c2e29f97648ce8598c", []},
"ibrowse": {:hex, :ibrowse, "4.2.2", "b32b5bafcc77b7277eff030ed32e1acc3f610c64e9f6aea19822abcadf681b4b", [:rebar3], []},

View File

@ -1,7 +1,24 @@
defmodule Farmbot.BotStateTest do
require IEx
use ExUnit.Case, async: false
setup_all do
unencoded = %{
"bot" => "device_4000",
"exp" => 123456,
"iat" => 848585,
"fw_update_server" => "rate_my_dog.org",
"os_update_server" => "rate_my_dog.org",
"jti" => "what is this?",
"sub" => "webmin@yahoo.com",
"mqtt" => "mqtt.att.net",
"iss" => "http://ibm.com"
}
fake_token = Token.create%{"unencoded" => unencoded, "encoded" => "asdf"}
send Farmbot.BotState.Authorization, {:authorization, fake_token}
Process.sleep(10)
{:ok, %{auth: fake_token}}
end
test("Gets the current bot position") do
[x,y,z] = Farmbot.BotState.get_current_pos
assert(is_integer(x) and is_integer(y) and is_integer(z))
@ -27,20 +44,107 @@ defmodule Farmbot.BotStateTest do
assert(value == 55)
end
test "updates a config" do
test "updates tons of configs" do
Farmbot.BotState.update_config("os_auto_update", false)
val = Farmbot.BotState.get_config(:os_auto_update)
assert(val == false)
os_auto_update = Farmbot.BotState.get_config(:os_auto_update)
assert(os_auto_update == false)
Farmbot.BotState.update_config("fw_auto_update", false)
fw_auto_update = Farmbot.BotState.get_config(:fw_auto_update)
assert(fw_auto_update == false)
Farmbot.BotState.update_config("timezone", "we dont even check this")
timezone = Farmbot.BotState.get_config(:timezone)
assert(timezone == "we dont even check this")
Farmbot.BotState.update_config("steps_per_mm", 9001)
steps_per_mm = Farmbot.BotState.get_config(:steps_per_mm)
assert(steps_per_mm == 9001)
fail = Farmbot.BotState.update_config("self_destruct_count_down", 10_000)
assert(fail == false)
end
test "gets the current os version" do
# i just want the coverage report ok
os = Farmbot.BotState.get_os_version
assert(is_bitstring(os))
end
test "sets and removes a lock" do
Farmbot.BotState.add_lock("e_stop")
v = Farmbot.BotState.get_lock("e_stop")
assert(v == 0)
assert(is_integer(v))
Farmbot.BotState.remove_lock("e_stop")
v = Farmbot.BotState.get_lock("e_stop")
assert(v == nil)
end
test "sets a lock and fails to set the same lock again" do
str = "Bot doesnt work on christmas."
Farmbot.BotState.add_lock(str)
old_locks = get_locks
Farmbot.BotState.add_lock(str)
new_locks = get_locks
assert(new_locks == old_locks)
end
defp get_locks do
# this is because the tracker modules arent that fast
# and other than testing there is not a use case where
# one needs to set a value (with a cast)
# and then get a value right after that (with a call)
# if i start needing to do this in production code i will handle it then.
Process.sleep(10)
StateDebugger.state
|> Map.get(:configuration)
|> Map.get(:locks)
end
test "fails to remove a locl" do
fail = Farmbot.BotState.remove_lock("my dog stepped on my bot")
assert(fail == {:error, :no_index})
end
test "sets end stops" do
es = {1,1,1,1,1,1}
Farmbot.BotState.set_end_stops(es)
assert(get_hardware_part(:end_stops) == es)
end
test "gets all the mcu params" do
assert get_hardware_part(:mcu_params) == Farmbot.BotState.get_all_mcu_params
end
defp get_hardware_part(part) do
Process.sleep(10)
StateDebugger.state
|> Map.get(:hardware)
|> Map.get(part)
end
test "gets the most recent token" do
assert get_auth_part(:token) == Farmbot.BotState.get_token
end
test "gets the api server url", context do
assert context.auth.unencoded.iss == Farmbot.BotState.get_server
end
test "adds credentials to auth" do
Farmbot.BotState.add_creds({"connor@farmbot.io", "plaintext_pass", "http://ibm.com"})
interim = get_auth_part(:interim)
assert interim.email == "connor@farmbot.io"
assert interim.pass == "plaintext_pass"
end
defp get_auth_part(part) do
Process.sleep(10)
StateDebugger.state
|> Map.get(:authorization)
|> Map.get(part)
end
end

View File

@ -0,0 +1,49 @@
defmodule Farmbot.BotState.ConfigurationTest do
use ExUnit.Case, async: false
alias Farmbot.BotState.Configuration.State, as: ConfigState
test "makes sure we init with good usable state" do
m = Farmbot.BotState.Configuration
# this is the bad write. it just puts some garbage into safestorage
# What should happen is the load function sees this as bad data, throws
# it away and just gives the initial state back
initial = %ConfigState{configuration: %{steps_per_mm: 6000}}
SafeStorage.write(m, :erlang.term_to_binary(%{fake: :stuff}))
bad_write = m.load(initial)
assert(bad_write == initial)
# Here we are writeing a good config state into storage so when we load it
# again it should persist
old = %ConfigState{configuration: %{steps_per_mm: 42}}
SafeStorage.write(m, :erlang.term_to_binary(initial))
good_write = m.load(old)
assert(good_write != old)
end
test "makes sure we dont mess state up with bad calls or casts" do
before_call = get_state
resp = GenServer.call(Farmbot.BotState.Configuration, :do_a_barrel_roll)
after_call = get_state
assert(resp == :unhandled)
assert(after_call == before_call)
GenServer.cast(Farmbot.BotState.Configuration, :bot_net_start)
after_cast = get_state
assert(before_call == after_cast)
end
test "updates a setting inside informational settings" do
old = get_state
GenServer.cast(Farmbot.BotState.Configuration,
{:update_info, :i_know_this, :its_unix})
# maybe bug? change this cast to a call?
new = get_state
assert(old != new)
end
defp get_state do
Process.sleep(10)
StateDebugger.state |> Map.get(:configuration)
end
end

View File

@ -0,0 +1,77 @@
defmodule AstTest do
use ExUnit.Case, async: false
# %{"args" => %{"message" => "hello world"},
# "body" =>
# [%{"args" => %{"channel_name" => "toast_error"}, "kind" => "channel"}], "kind" => "send_message"}
test "parses an ast from a stringed map" do
test_ast =
%{"args" => %{"message" => "hello world"},
"body" =>[],
"kind" => "send_message"}
ast = Ast.parse(test_ast)
assert(ast.args.message == "hello world")
assert(ast.body == [])
assert(ast.kind == "send_message")
end
test "parses an ast from a stringed map with no body" do
test_ast =
%{"args" => %{"coords" => [1,2,3]},
"kind" => "pickup_truck"}
ast = Ast.parse(test_ast)
assert(ast.args.coords == [1,2,3])
assert(ast.body == [])
assert(ast.kind == "pickup_truck")
end
test "parses an ast from a keyed map" do
test_ast_a =
%{args: %{},
kind: "play_kick_ball"}
ast_a = Ast.parse(test_ast_a)
assert(ast_a.args == %{})
assert(ast_a.body == [])
assert(ast_a.kind == "play_kick_ball")
test_ast_b =
%{args: %{},
body: [],
kind: "play_kick_ball"}
ast_b = Ast.parse(test_ast_b)
assert(ast_b.args == %{})
assert(ast_b.body == [])
assert(ast_b.kind == "play_kick_ball")
end
test "creats more ast nodes from the body" do
test_ast_body_inner = %{
args: %{},
body: [],
kind: "inner_body_node"
}
test_ast_body_main = %{
args: %{},
body: [test_ast_body_inner, test_ast_body_inner],
kind: "body_node"
}
test_ast_main =
%{args: %{},
body: [test_ast_body_main, test_ast_body_inner, test_ast_body_main],
kind: "play_kick_ball"}
main_ast = Ast.parse(test_ast_main)
assert(main_ast.args == %{})
assert(main_ast.kind == "play_kick_ball")
body = main_ast.body
assert(is_list(body))
assert(Enum.count(body) == 3)
# there may need to be a protection for a stack overflow here lol
# if you could somehow make a node that references itself or someting
# similar to that it would recurse forever
random_ast_node = Enum.random(body)
assert(random_ast_node.kind |> is_bitstring)
end
end

View File

@ -0,0 +1,23 @@
defmodule CorpusTest do
@moduledoc false
use ExUnit.Case, async: true
test "builds a corpus" do
not_fail =
Corpus.create(%{
"tag" => 0,
"args" => %{},
"nodes" => %{}
})
assert(not_fail.tag == 0)
assert(not_fail.args == %{})
assert(not_fail.nodes == %{})
end
test "does not build a corpus" do
fail = Corpus.create(%{"fake" => "corpus"})
also_fail = Corpus.create(:wrong_type)
assert(fail == :error)
assert(also_fail == :error)
end
end

View File

@ -0,0 +1,25 @@
defmodule DeviceTest do
@moduledoc false
use ExUnit.Case, async: true
test "builds a device" do
not_fail =
Device.create(%{
"id" => 123,
"planting_area_id" => 321,
"name" => "lunch_time",
"webcam_url" => nil
})
assert(not_fail.id == 123)
assert(not_fail.planting_area_id == 321)
assert(not_fail.name == "lunch_time")
assert(not_fail.webcam_url == nil)
end
test "does not build a device" do
fail = Device.create(%{"fake" => "device"})
also_fail = Device.create(:wrong_type)
assert(fail == :error)
assert(also_fail == :error)
end
end

View File

@ -0,0 +1,30 @@
defmodule PeripheralTest do
@moduledoc false
use ExUnit.Case, async: true
test "builds a Peripheral" do
not_fail =
Peripheral.create(%{
"id" => 123,
"device_id" => 965,
"pin" => 25,
"mode" => 0,
"label" => "laser beam",
"created_at" => "timestamp hur hur hur",
"updated_at" => "timestamp hur hur hur"})
assert(not_fail.id == 123)
assert(not_fail.device_id == 965)
assert(not_fail.pin == 25)
assert(not_fail.mode == 0)
assert(not_fail.label == "laser beam")
assert(not_fail.created_at == "timestamp hur hur hur")
assert(not_fail.updated_at == "timestamp hur hur hur")
end
test "does not build a Peripheral" do
fail = Peripheral.create(%{"fake" => "Peripheral"})
also_fail = Peripheral.create(:wrong_type)
assert(fail == :error)
assert(also_fail == :error)
end
end