Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F85629167
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Size
5 KB
Referenced Files
None
Subscribers
None
View Options
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
Details
Attached
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)
Attached To
Mode
R14 exile
Attached
Detach File
Event Timeline
Log In to Comment