227 lines
5.9 KiB
Elixir
227 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
|