From 5def8d40e83f8685f942020ec2502adb4ddd49a2 Mon Sep 17 00:00:00 2001 From: Sergey Kukunin Date: Mon, 16 Oct 2017 17:54:44 +0300 Subject: [PATCH 1/7] Describe local server launch in README.md --- README.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 7374f59..09954a8 100644 --- a/README.md +++ b/README.md @@ -63,7 +63,17 @@ Application.ensure_all_started(:hound) ExUnit.start() ``` -When you run `mix test`, Hound is automatically started. __You'll need a webdriver server__ running, like Selenium Server or Chrome Driver. If you aren't sure what it is, then [read this](https://github.com/HashNuke/hound/wiki/Starting-a-webdriver-server). +* Hound does *NOT* start local server automatically. You need to handle it on your own. +If you use Phoenix, you can edit `config/test.exs` and set `server: true` to launch server +automatically: + +```elixir +config :myapp, MyAppWeb.Endpoint, + http: [port: 4001], + server: true +``` + +* When you run `mix test`, Hound is automatically started, but __you'll need a webdriver server__ running, like Selenium Server or Chrome Driver. If you aren't sure what it is, then [read this](https://github.com/HashNuke/hound/wiki/Starting-a-webdriver-server). ## Configure From 1844fede594d3407abeec62e0269b67d75a99064 Mon Sep 17 00:00:00 2001 From: Sergey Kukunin Date: Mon, 17 Dec 2018 06:58:39 +0200 Subject: [PATCH 2/7] Fixes #226, raises an exception on webdriver errors --- lib/hound/request_utils.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/hound/request_utils.ex b/lib/hound/request_utils.ex index a048f6f..4f72463 100644 --- a/lib/hound/request_utils.ex +++ b/lib/hound/request_utils.ex @@ -55,7 +55,7 @@ defmodule Hound.RequestUtils do end defp handle_response({:error, reason}, _, _) do - {:error, reason} + raise Hound.Error, "Webdriver request error: #{inspect(reason)}" end defp response_parser do From af21f0f7528564b98fd60482052f4618b15fbe35 Mon Sep 17 00:00:00 2001 From: Sergey Kukunin Date: Mon, 17 Dec 2018 07:09:29 +0200 Subject: [PATCH 3/7] Don't crash the whole Hound.SessionServer on a single exception --- lib/hound/session_server.ex | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/hound/session_server.ex b/lib/hound/session_server.ex index 660ebff..95caa5e 100644 --- a/lib/hound/session_server.ex +++ b/lib/hound/session_server.ex @@ -35,7 +35,9 @@ defmodule Hound.SessionServer do def change_current_session_for_pid(pid, session_name, opts) do - GenServer.call(@name, {:change_session, pid, session_name, opts}, 60000) + {:ok, session_id} = + GenServer.call(@name, {:change_session, pid, session_name, opts}, 60000) + session_id end @@ -80,7 +82,9 @@ defmodule Hound.SessionServer do end :ets.insert(@name, {pid, ref, session_id, sessions}) - {:reply, session_id, Map.put(state, ref, pid)} + {:reply, {:ok, session_id}, Map.put(state, ref, pid)} + rescue + error -> {:reply, {:error, error}, state} end def handle_call({:destroy_sessions, pid}, _from, state) do From 06d2907b8daefbd26beeff626d89b652ba89bb8e Mon Sep 17 00:00:00 2001 From: Sergey Kukunin Date: Tue, 18 Dec 2018 11:32:18 +0200 Subject: [PATCH 4/7] Don't block on destroy sessions --- lib/hound/session_server.ex | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/hound/session_server.ex b/lib/hound/session_server.ex index 95caa5e..1a1ef8e 100644 --- a/lib/hound/session_server.ex +++ b/lib/hound/session_server.ex @@ -110,7 +110,12 @@ defmodule Hound.SessionServer do sessions = all_sessions_for_pid(pid) :ets.delete(@name, pid) Enum.each sessions, fn({_session_name, session_id})-> - Hound.Session.destroy_session(session_id) + spawn fn -> destroy_session(session_id) end end end + + defp destroy_session(session_id) do + Hound.Session.destroy_session(session_id) + rescue Hound.Error -> false + end end From d95f8b56c34b836249cbee678d2e62a56c666dac Mon Sep 17 00:00:00 2001 From: Sergey Kukunin Date: Tue, 18 Dec 2018 12:55:48 +0200 Subject: [PATCH 5/7] Implement Hound.SessionServer.all_sessions function --- lib/hound/session_server.ex | 5 +++++ test/hound_test.exs | 5 ++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/hound/session_server.ex b/lib/hound/session_server.ex index 1a1ef8e..0d1a29d 100644 --- a/lib/hound/session_server.ex +++ b/lib/hound/session_server.ex @@ -49,6 +49,11 @@ defmodule Hound.SessionServer do end + def all_sessions do + :ets.foldl(fn {_pid, _ref, _session_id, sessions}, acc -> acc ++ Map.values(sessions) end, [], @name) + end + + def destroy_sessions_for_pid(pid) do GenServer.call(@name, {:destroy_sessions, pid}, 60000) end diff --git a/test/hound_test.exs b/test/hound_test.exs index ca65e81..81ba3d2 100644 --- a/test/hound_test.exs +++ b/test/hound_test.exs @@ -19,8 +19,11 @@ defmodule HoundTest do assert is_binary(Hound.current_session_id) end + test "should return all sessions" do + assert Hound.SessionServer.all_sessions |> Enum.member?(Hound.current_session_id) + end - test "Should destroy all sessions for current process" do + test "should destroy all sessions for current process" do Hound.end_session assert Hound.SessionServer.all_sessions_for_pid(self()) == %{} end From 811b3e50a7f4513ba5cb5ce63591f0607ec8d06c Mon Sep 17 00:00:00 2001 From: Sergey Kukunin Date: Tue, 18 Dec 2018 13:01:57 +0200 Subject: [PATCH 6/7] Don't keep session monitor ref in :ets --- lib/hound/session_server.ex | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/lib/hound/session_server.ex b/lib/hound/session_server.ex index 0d1a29d..ef730b9 100644 --- a/lib/hound/session_server.ex +++ b/lib/hound/session_server.ex @@ -17,14 +17,14 @@ defmodule Hound.SessionServer do def current_session_id(pid) do case :ets.lookup(@name, pid) do - [{^pid, _ref, session_id, _all_sessions}] -> session_id + [{^pid, session_id, _all_sessions}] -> session_id [] -> nil end end def current_session_name(pid) do case :ets.lookup(@name, pid) do - [{^pid, _ref, session_id, all_sessions}] -> + [{^pid, session_id, all_sessions}] -> Enum.find_value all_sessions, fn {name, id} when id == session_id -> name _ -> nil @@ -43,14 +43,14 @@ defmodule Hound.SessionServer do def all_sessions_for_pid(pid) do case :ets.lookup(@name, pid) do - [{^pid, _ref, _session_id, all_sessions}] -> all_sessions + [{^pid, _session_id, all_sessions}] -> all_sessions [] -> %{} end end def all_sessions do - :ets.foldl(fn {_pid, _ref, _session_id, sessions}, acc -> acc ++ Map.values(sessions) end, [], @name) + :ets.foldl(fn {_pid, _session_id, sessions}, acc -> acc ++ Map.values(sessions) end, [], @name) end @@ -69,12 +69,11 @@ defmodule Hound.SessionServer do def handle_call({:change_session, pid, session_name, opts}, _from, state) do {:ok, driver_info} = Hound.driver_info - {ref, sessions} = + sessions = case :ets.lookup(@name, pid) do - [{^pid, ref, _session_id, sessions}] -> - {ref, sessions} - [] -> - {Process.monitor(pid), %{}} + [{^pid, _session_id, sessions}] -> + sessions + [] -> %{} end {session_id, sessions} = @@ -86,8 +85,8 @@ defmodule Hound.SessionServer do {session_id, Map.put(sessions, session_name, session_id)} end - :ets.insert(@name, {pid, ref, session_id, sessions}) - {:reply, {:ok, session_id}, Map.put(state, ref, pid)} + :ets.insert(@name, {pid, session_id, sessions}) + {:reply, {:ok, session_id}, monitor_session(pid, state)} rescue error -> {:reply, {:error, error}, state} end @@ -104,6 +103,15 @@ defmodule Hound.SessionServer do {:noreply, state} end + defp monitor_session(pid, state) do + if state |> Map.values |> Enum.member?(pid) do + state + else + ref = Process.monitor(pid) + Map.put(state, ref, pid) + end + end + defp create_session(driver_info, opts) do case Hound.Session.create_session(driver_info[:browser], opts) do {:ok, session_id} -> session_id From 520786a8f925da21ba2d768957736bd66f672ea1 Mon Sep 17 00:00:00 2001 From: Sergey Kukunin Date: Tue, 18 Dec 2018 13:13:21 +0200 Subject: [PATCH 7/7] Create session outside of SessionServer It is not a bottleneck anymore --- lib/hound/session_server.ex | 45 +++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 24 deletions(-) diff --git a/lib/hound/session_server.ex b/lib/hound/session_server.ex index ef730b9..3d434ad 100644 --- a/lib/hound/session_server.ex +++ b/lib/hound/session_server.ex @@ -35,8 +35,25 @@ defmodule Hound.SessionServer do def change_current_session_for_pid(pid, session_name, opts) do - {:ok, session_id} = - GenServer.call(@name, {:change_session, pid, session_name, opts}, 60000) + {:ok, driver_info} = Hound.driver_info + + sessions = + case :ets.lookup(@name, pid) do + [{^pid, _session_id, sessions}] -> + sessions + [] -> %{} + end + + {session_id, sessions} = + case Map.fetch(sessions, session_name) do + {:ok, session_id} -> + {session_id, sessions} + :error -> + session_id = create_session(driver_info, opts) + {session_id, Map.put(sessions, session_name, session_id)} + end + + :ok = GenServer.call(@name, {:register, pid, session_id, sessions}, 60000) session_id end @@ -66,29 +83,9 @@ defmodule Hound.SessionServer do end - def handle_call({:change_session, pid, session_name, opts}, _from, state) do - {:ok, driver_info} = Hound.driver_info - - sessions = - case :ets.lookup(@name, pid) do - [{^pid, _session_id, sessions}] -> - sessions - [] -> %{} - end - - {session_id, sessions} = - case Map.fetch(sessions, session_name) do - {:ok, session_id} -> - {session_id, sessions} - :error -> - session_id = create_session(driver_info, opts) - {session_id, Map.put(sessions, session_name, session_id)} - end - + def handle_call({:register, pid, session_id, sessions}, _from, state) do :ets.insert(@name, {pid, session_id, sessions}) - {:reply, {:ok, session_id}, monitor_session(pid, state)} - rescue - error -> {:reply, {:error, error}, state} + {:reply, :ok, monitor_session(pid, state)} end def handle_call({:destroy_sessions, pid}, _from, state) do