Page MenuHomePhorge

No OneTemporary

Size
5 KB
Referenced Files
None
Subscribers
None
diff --git a/.gitignore b/.gitignore
index d4d2f75..7066211 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,24 +1,25 @@
# The directory Mix will write compiled artifacts to.
/_build/
# If you run "mix test --cover", coverage assets end up here.
/cover/
# The directory Mix downloads your dependencies sources to.
/deps/
# Where 3rd-party dependencies like ExDoc output generated docs.
/doc/
# Ignore .fetch files in case you like to edit your project deps locally.
/.fetch
# If the VM crashes, it generates a dump, let's ignore it too.
erl_crash.dump
# Also ignore archive artifacts (built via "mix archive.build").
*.ez
# Ignore package tarball (built via "mix hex.build").
exile-*.tar
+.dir-locals.el
\ No newline at end of file
diff --git a/test/exile/process_test.exs b/test/exile/process_test.exs
new file mode 100644
index 0000000..149b91f
--- /dev/null
+++ b/test/exile/process_test.exs
@@ -0,0 +1,146 @@
+defmodule Exile.ProcessTest do
+ use ExUnit.Case, async: true
+ alias Exile.Process
+
+ test "read" do
+ {:ok, s} = Process.start_link("echo", ["test"])
+ assert {:ok, "test\n"} == Process.read(s)
+ assert {:eof, []} == Process.read(s)
+ assert :ok == Process.close_stdin(s)
+ assert {:ok, {:exit, 0}} == Process.await_exit(s, 500)
+ end
+
+ test "write" do
+ {:ok, s} = Process.start_link("cat", [])
+ assert :ok == Process.write(s, "hello")
+ assert {:ok, "hello"} == Process.read(s)
+ assert :ok == Process.write(s, "world")
+ assert {:ok, "world"} == Process.read(s)
+ assert :ok == Process.close_stdin(s)
+ assert {:eof, []} == Process.read(s)
+ assert {:ok, {:exit, 0}} == Process.await_exit(s, 100)
+ end
+
+ test "stdin close" do
+ logger = start_events_collector()
+
+ # base64 produces output only after getting EOF from stdin. we
+ # collect events in order and assert that we can still read from
+ # stdout even after closing stdin
+ {:ok, s} = Process.start_link("base64", [])
+
+ # parallel reader should be blocked till we close stdin
+ start_parallel_reader(s, logger)
+ :timer.sleep(50)
+
+ assert :ok == Process.write(s, "hello")
+ add_event(logger, {:write, "hello"})
+ assert :ok == Process.write(s, "world")
+ add_event(logger, {:write, "world"})
+ :timer.sleep(50)
+
+ assert :ok == Process.close_stdin(s)
+ add_event(logger, :input_close)
+ assert {:ok, {:exit, 0}} == Process.await_exit(s, 50)
+
+ assert [
+ {:write, "hello"},
+ {:write, "world"},
+ :input_close,
+ {:read, "aGVsbG93b3JsZA==\n"},
+ :eof
+ ] == get_events(logger)
+ end
+
+ test "external command termination on stop" do
+ {:ok, s} = Process.start_link("cat", [])
+ {:ok, os_pid} = Process.os_pid(s)
+ assert os_process_alive?(os_pid)
+
+ Process.stop(s)
+ :timer.sleep(100)
+
+ refute os_process_alive?(os_pid)
+ end
+
+ test "external command kill on stop" do
+ # cat command hangs waiting for EOF
+ {:ok, s} = Process.start_link(fixture("ignore_sigterm.sh"), [])
+
+ {:ok, os_pid} = Process.os_pid(s)
+ assert os_process_alive?(os_pid)
+ Process.stop(s)
+
+ if os_process_alive?(os_pid) do
+ :timer.sleep(3000)
+ refute os_process_alive?(os_pid)
+ else
+ :ok
+ end
+ end
+
+ test "exit status" do
+ {:ok, s} = Process.start_link(fixture("exit_with.sh"), ["2"])
+ assert {:ok, {:exit, 2}} == Process.await_exit(s, 200)
+ end
+
+ test "process kill with pending write" do
+ {:ok, s} = Process.start_link("cat", [])
+ {:ok, os_pid} = Process.os_pid(s)
+
+ large_data =
+ Stream.cycle(["test"]) |> Stream.take(500_000) |> Enum.to_list() |> IO.iodata_to_binary()
+
+ task =
+ Task.async(fn ->
+ try do
+ Process.write(s, large_data)
+ catch
+ :exit, reason -> reason
+ end
+ end)
+
+ :timer.sleep(200)
+ Process.stop(s)
+ :timer.sleep(100)
+
+ refute os_process_alive?(os_pid)
+ assert {:normal, _} = Task.await(task)
+ end
+
+ def start_parallel_reader(proc_server, logger) do
+ spawn_link(fn -> reader_loop(proc_server, logger) end)
+ end
+
+ def reader_loop(proc_server, logger) do
+ case Process.read(proc_server) do
+ {:ok, data} ->
+ add_event(logger, {:read, data})
+ reader_loop(proc_server, logger)
+
+ {:eof, []} ->
+ add_event(logger, :eof)
+ end
+ end
+
+ def start_events_collector do
+ {:ok, ordered_events} = Agent.start(fn -> [] end)
+ ordered_events
+ end
+
+ def add_event(agent, event) do
+ :ok = Agent.update(agent, fn events -> events ++ [event] end)
+ end
+
+ def get_events(agent) do
+ Agent.get(agent, & &1)
+ end
+
+ defp os_process_alive?(pid) do
+ match?({_, 0}, System.cmd("ps", ["-p", to_string(pid)]))
+ end
+
+ defp fixture(script) do
+ Path.join([__DIR__, "../scripts", script])
+ end
+end
diff --git a/test/scripts/exit_with.sh b/test/scripts/exit_with.sh
new file mode 100755
index 0000000..8ec3f91
--- /dev/null
+++ b/test/scripts/exit_with.sh
@@ -0,0 +1,2 @@
+#!/bin/bash
+exit 2
diff --git a/test/scripts/ignore_sigterm.sh b/test/scripts/ignore_sigterm.sh
new file mode 100755
index 0000000..67d05d3
--- /dev/null
+++ b/test/scripts/ignore_sigterm.sh
@@ -0,0 +1,7 @@
+#!/bin/bash
+
+trap -- '' SIGINT SIGTERM SIGTSTP
+while true; do
+ date +%F_%T
+ sleep 1
+done
diff --git a/test/test_helper.exs b/test/test_helper.exs
index 869559e..6a0af57 100644
--- a/test/test_helper.exs
+++ b/test/test_helper.exs
@@ -1 +1 @@
-ExUnit.start()
+ExUnit.start(capture_log: true)

File Metadata

Mime Type
text/x-diff
Expires
Sat, Aug 8, 6:06 PM (1 d, 18 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1722658
Default Alt Text
(5 KB)

Event Timeline