farmbot_os/farmbot_celery_script/test/farmbot_celery_script/run_time_test.exs

231 lines
5.9 KiB
Elixir

defmodule Farmbot.CeleryScript.RunTimeTest do
use ExUnit.Case
alias Farmbot.CeleryScript.RunTime
import Farmbot.CeleryScript.Utils
alias Farmbot.CeleryScript.AST
test "simple rpc_request returns rpc_ok" do
pid = self()
io_fun = fn ast ->
send(pid, ast)
:ok
end
hyper_fun = fn _ -> :ok end
name = __ENV__.function |> elem(0)
opts = [
process_io_layer: io_fun,
hyper_io_layer: hyper_fun
]
{:ok, farmbot_celery_script} = RunTime.start_link(opts, name)
label = to_string(name)
ast = ast(:rpc_request, %{label: label}, [ast(:wait, %{milliseconds: 100})])
RunTime.rpc_request(farmbot_celery_script, ast, fn result_ast ->
send(pid, result_ast)
end)
assert_receive %AST{kind: :wait, args: %{milliseconds: 100}}
assert_receive %AST{kind: :rpc_ok, args: %{label: ^label}}
end
test "simple rpc_request returns rpc_error" do
pid = self()
io_fun = fn ast ->
send(pid, ast)
{:error, "reason"}
end
hyper_fun = fn _ -> :ok end
name = __ENV__.function |> elem(0)
opts = [
process_io_layer: io_fun,
hyper_io_layer: hyper_fun
]
{:ok, farmbot_celery_script} = RunTime.start_link(opts, name)
label = to_string(name)
ast = ast(:rpc_request, %{label: label}, [ast(:wait, %{milliseconds: 100})])
RunTime.rpc_request(farmbot_celery_script, ast, fn result_ast ->
send(pid, result_ast)
end)
assert_receive %AST{kind: :wait, args: %{milliseconds: 100}}
assert_receive %AST{
kind: :rpc_error,
args: %{label: ^label},
body: [%AST{kind: :explanation, args: %{message: "reason"}}]
}
end
test "rpc_request requires `label` argument" do
assert_raise ArgumentError, fn ->
# don't need to start a vm here, since this shouldn't actual call the vm.
RunTime.rpc_request(ast(:rpc_request, %{}, []), fn _ -> :ok end)
end
end
test "emergency_lock and emergency_unlock" do
pid = self()
io_fun = fn _ast -> :ok end
hyper_fun = fn hyper -> send(pid, hyper) end
name = __ENV__.function |> elem(0)
opts = [
process_io_layer: io_fun,
hyper_io_layer: hyper_fun
]
{:ok, farmbot_celery_script} = RunTime.start_link(opts, name)
lock_ast = ast(:rpc_request, %{label: name}, [ast(:emergency_lock, %{})])
RunTime.rpc_request(farmbot_celery_script, lock_ast, io_fun)
assert_receive :emergency_lock
unlock_ast =
ast(:rpc_request, %{label: name}, [ast(:emergency_unlock, %{})])
RunTime.rpc_request(farmbot_celery_script, unlock_ast, io_fun)
assert_receive :emergency_unlock
end
test "rpc_requests get queued" do
pid = self()
io_fun = fn %{kind: :wait, args: %{milliseconds: secs}} ->
Process.sleep(secs)
:ok
end
hyper_fun = fn _ -> :ok end
name = __ENV__.function |> elem(0)
opts = [
process_io_layer: io_fun,
hyper_io_layer: hyper_fun
]
{:ok, farmbot_celery_script} = RunTime.start_link(opts, name)
to = 500
label1 = "one"
label2 = "two"
ast1 =
ast(:rpc_request, %{label: label1}, [ast(:wait, %{milliseconds: to})])
ast2 =
ast(:rpc_request, %{label: label2}, [ast(:wait, %{milliseconds: to})])
cb = fn %{kind: :rpc_ok} = rpc_ok -> send(pid, rpc_ok) end
spawn_link(RunTime, :rpc_request, [farmbot_celery_script, ast1, cb])
spawn_link(RunTime, :rpc_request, [farmbot_celery_script, ast2, cb])
rpc_ok1 = ast(:rpc_ok, %{label: label1})
rpc_ok2 = ast(:rpc_ok, %{label: label2})
refute_received ^rpc_ok1
refute_received ^rpc_ok2
assert_receive ^rpc_ok2, to * 2
assert_receive ^rpc_ok1, to * 2
end
test "farm_proc step doesn't crash farmbot_celery_script" do
pid = self()
io_fun = fn _ast ->
raise("oh noes!!")
end
hyper_fun = fn _ -> :ok end
name = __ENV__.function |> elem(0)
opts = [
process_io_layer: io_fun,
hyper_io_layer: hyper_fun
]
{:ok, farmbot_celery_script} = RunTime.start_link(opts, name)
ast = ast(:rpc_request, %{label: name}, [ast(:wait, %{})])
RunTime.rpc_request(farmbot_celery_script, ast, fn rpc_err -> send(pid, rpc_err) end)
assert_receive %AST{
kind: :rpc_error,
args: %{label: ^name},
body: [%AST{kind: :explanation, args: %{message: "oh noes!!"}}]
}
end
test "farmbot_celery_script callbacks with exception won't crash farmbot_celery_script" do
pid = self()
io_fun = fn _ast -> :ok end
hyper_fun = fn _ -> :ok end
name = __ENV__.function |> elem(0)
opts = [
process_io_layer: io_fun,
hyper_io_layer: hyper_fun
]
{:ok, farmbot_celery_script} = RunTime.start_link(opts, name)
ast = ast(:rpc_request, %{label: name}, [])
RunTime.rpc_request(farmbot_celery_script, ast, fn rpc_ok ->
send(pid, rpc_ok)
raise("bye!")
end)
assert_receive %AST{
kind: :rpc_ok,
args: %{label: ^name}
}
end
test "farmbot_celery_script sequence executes callback async" do
pid = self()
io_fun = fn ast ->
send(pid, ast)
case ast.kind do
:wait -> :ok
:send_message -> {:error, "whoops!"}
end
end
hyper_fun = fn _ -> :ok end
name = __ENV__.function |> elem(0)
opts = [
process_io_layer: io_fun,
hyper_io_layer: hyper_fun
]
{:ok, farmbot_celery_script} = RunTime.start_link(opts, name)
ok_ast = ast(:sequence, %{id: 100}, [ast(:wait, %{milliseconds: 100})])
err_ast =
ast(:sequence, %{id: 101}, [ast(:send_message, %{message: "???"})])
cb = fn results -> send(pid, results) end
vm_pid = RunTime.sequence(farmbot_celery_script, ok_ast, 100, cb)
assert Process.alive?(vm_pid)
assert_receive %AST{kind: :wait, args: %{milliseconds: 100}}
assert_receive :ok
vm_pid = RunTime.sequence(farmbot_celery_script, err_ast, 101, cb)
assert Process.alive?(vm_pid)
assert_receive %AST{kind: :send_message, args: %{message: "???"}}
assert_receive {:error, "whoops!"}
end
end