Current section

Files

Jump to
dream_test src dream_test@sandbox.erl
Raw

src/dream_test@sandbox.erl

-module(dream_test@sandbox).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/dream_test/sandbox.gleam").
-export([run_isolated/2, default_config/0]).
-export_type([sandbox_config/0, sandbox_result/0, worker_message/0]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
-type sandbox_config() :: {sandbox_config, integer()}.
-type sandbox_result() :: {sandbox_completed,
dream_test@types:assertion_result()} |
sandbox_timed_out |
{sandbox_crashed, binary()}.
-type worker_message() :: {test_completed, dream_test@types:assertion_result()} |
{worker_down, binary()}.
-file("src/dream_test/sandbox.gleam", 52).
?DOC(" Spawn a worker process that runs the test and sends the result.\n").
-spec spawn_worker(
gleam@erlang@process:subject(dream_test@types:assertion_result()),
fun(() -> dream_test@types:assertion_result())
) -> gleam@erlang@process:pid_().
spawn_worker(Result_subject, Test_function) ->
proc_lib:spawn(
fun() ->
Result = Test_function(),
gleam@erlang@process:send(Result_subject, Result)
end
).
-file("src/dream_test/sandbox.gleam", 73).
?DOC(" Map a test result to a WorkerMessage.\n").
-spec map_result_to_message(dream_test@types:assertion_result()) -> worker_message().
map_result_to_message(Result) ->
{test_completed, Result}.
-file("src/dream_test/sandbox.gleam", 84).
?DOC(" Format an exit reason as a string for error reporting.\n").
-spec format_exit_reason(gleam@erlang@process:exit_reason()) -> binary().
format_exit_reason(Reason) ->
case Reason of
normal ->
<<"normal"/utf8>>;
killed ->
<<"killed"/utf8>>;
{abnormal, Dynamic_reason} ->
gleam@string:inspect(Dynamic_reason)
end.
-file("src/dream_test/sandbox.gleam", 78).
?DOC(" Map a process down event to a WorkerMessage.\n").
-spec map_down_to_message(gleam@erlang@process:down()) -> worker_message().
map_down_to_message(Down) ->
Reason = format_exit_reason(erlang:element(4, Down)),
{worker_down, Reason}.
-file("src/dream_test/sandbox.gleam", 63).
?DOC(" Build a selector that waits for either test completion or process down.\n").
-spec build_result_selector(
gleam@erlang@process:subject(dream_test@types:assertion_result())
) -> gleam@erlang@process:selector(worker_message()).
build_result_selector(Result_subject) ->
_pipe = gleam_erlang_ffi:new_selector(),
_pipe@1 = gleam@erlang@process:select(_pipe, Result_subject),
_pipe@2 = gleam_erlang_ffi:map_selector(
_pipe@1,
fun map_result_to_message/1
),
gleam@erlang@process:select_monitors(_pipe@2, fun map_down_to_message/1).
-file("src/dream_test/sandbox.gleam", 106).
?DOC(" Handle a message from the worker or monitor.\n").
-spec handle_worker_message(worker_message()) -> sandbox_result().
handle_worker_message(Message) ->
case Message of
{test_completed, Result} ->
{sandbox_completed, Result};
{worker_down, Reason} ->
{sandbox_crashed, Reason}
end.
-file("src/dream_test/sandbox.gleam", 114).
?DOC(" Handle timeout by killing the worker and returning timeout result.\n").
-spec handle_timeout(gleam@erlang@process:pid_()) -> sandbox_result().
handle_timeout(Worker_pid) ->
gleam@erlang@process:kill(Worker_pid),
sandbox_timed_out.
-file("src/dream_test/sandbox.gleam", 93).
?DOC(" Wait for the worker to complete or timeout.\n").
-spec wait_for_result(
sandbox_config(),
gleam@erlang@process:selector(worker_message()),
gleam@erlang@process:pid_(),
gleam@erlang@process:monitor()
) -> sandbox_result().
wait_for_result(Config, Selector, Worker_pid, _) ->
case gleam_erlang_ffi:select(Selector, erlang:element(2, Config)) of
{ok, Message} ->
handle_worker_message(Message);
{error, nil} ->
handle_timeout(Worker_pid)
end.
-file("src/dream_test/sandbox.gleam", 39).
?DOC(
" Run a test function in an isolated process with timeout.\n"
"\n"
" The test function runs in a separate BEAM process that is monitored.\n"
" If the process completes normally, its result is returned.\n"
" If the process crashes or times out, an appropriate SandboxResult is returned.\n"
).
-spec run_isolated(
sandbox_config(),
fun(() -> dream_test@types:assertion_result())
) -> sandbox_result().
run_isolated(Config, Test_function) ->
Result_subject = gleam@erlang@process:new_subject(),
Worker_pid = spawn_worker(Result_subject, Test_function),
Worker_monitor = gleam@erlang@process:monitor(Worker_pid),
Selector = build_result_selector(Result_subject),
wait_for_result(Config, Selector, Worker_pid, Worker_monitor).
-file("src/dream_test/sandbox.gleam", 120).
?DOC(" Default configuration with a 5 second timeout.\n").
-spec default_config() -> sandbox_config().
default_config() ->
{sandbox_config, 5000}.