/home/runner/work/chrme/chrme/_build/test/cover/eunit/chrme_launcher.html

1 -module(chrme_launcher).
2 -behaviour(gen_server).
3
4 -export([start/1, start_link/1, await_start/1, stop/1]).
5 -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2]).
6 -export_type([
7 options/0
8 , state/0
9 , name/0
10 ]).
11
12 -type name() :: term().
13
14 -type options() :: #{
15 name := name()
16 , executable => klsn:binstr()
17 , remote_port => 0..65535
18 , user_data_dir => klsn:binstr()
19 , extra_args => [klsn:binstr()]
20 , headless => boolean()
21 }.
22
23 -type state() :: #{
24 port := port()
25 , opts := options()
26 }.
27
28 -spec start(options()) -> {ok, pid()} | {error, term()}.
29 start(Options) ->
30
:-(
Name = maps:get(name, Options),
31
:-(
gen_server:start({global, Name}, ?MODULE, Options, []).
32
33 -spec start_link(options()) -> {ok, pid()} | {error, term()}.
34 start_link(Options) ->
35
:-(
Name = maps:get(name, Options),
36
:-(
gen_server:start_link({global, Name}, ?MODULE, Options, []).
37
38 -spec await_start(name()) -> ok.
39 await_start(Name) ->
40 % This also makes sure server exists.
41
:-(
Options = gen_server:call({global, Name}, get_options),
42
:-(
Host = <<"localhost">>,
43
:-(
Port = maps:get(remote_port, Options),
44
:-(
case chrme_http_apic:version(Host, Port) of
45 {ok, _} ->
46
:-(
ok;
47 _Error ->
48
:-(
timer:sleep(100),
49
:-(
await_start(Name)
50 end.
51
52 -spec stop(name()) -> ok.
53 stop(Name) ->
54
:-(
gen_server:stop({global, Name}).
55
56 -spec init(options()) -> {ok, state()}.
57 init(Options0) ->
58
:-(
Options = normalize_options(Options0),
59
:-(
PathBin = maps:get(executable, Options),
60
:-(
Path = binary_to_list(PathBin),
61
:-(
RemotePort = maps:get(remote_port, Options),
62
:-(
RemotePortStr = integer_to_list(RemotePort),
63
:-(
DataDirBin = maps:get(user_data_dir, Options),
64
:-(
DataDir = binary_to_list(DataDirBin),
65
:-(
Headless = maps:get(headless, Options),
66
:-(
ExtraArgsBin = maps:get(extra_args, Options),
67
:-(
ExtraArgs = [binary_to_list(B) || B <- ExtraArgsBin],
68
:-(
BaseArgs = [
69 "--remote-debugging-port=" ++ RemotePortStr,
70 "--user-data-dir=" ++ DataDir
71 ],
72
:-(
HeadlessArgs = case Headless of
73
:-(
true -> ["--headless"];
74
:-(
false -> []
75 end,
76
:-(
CmdArgs = BaseArgs ++ HeadlessArgs ++ ExtraArgs,
77
:-(
process_flag(trap_exit, true),
78
:-(
Port = open_port({spawn_executable, Path}, [
79 binary,
80 exit_status,
81 use_stdio,
82 stderr_to_stdout,
83 {args, CmdArgs}
84 ]),
85
:-(
{ok, #{port => Port, opts => Options}}.
86
87 % Internal: merge user options with defaults
88 -spec normalize_options(map()) -> options().
89 normalize_options(Options) ->
90
:-(
#{
91 name => maps:get(name, Options)
92 , executable => maps:get(executable, Options, <<"/usr/bin/google-chrome">>)
93 , remote_port => maps:get(remote_port, Options, 9222)
94 , user_data_dir => maps:get(user_data_dir, Options, <<"/tmp/chrme_default_user_data_dir">>)
95 , extra_args => maps:get(extra_args, Options, [])
96 , headless => maps:get(headless, Options, true)
97 }.
98
99 handle_call(get_options, _From, State) ->
100
:-(
{reply, maps:get(opts, State), State}.
101
102 handle_cast(_Msg, State) ->
103
:-(
{noreply, State}.
104
105 handle_info({Port, {data, _Data}}, State = #{port := Port}) ->
106
:-(
{noreply, State};
107 handle_info({Port, {exit_status, Status}}, State = #{port := Port}) ->
108
:-(
{stop, {chrome_exit, Status}, State};
109 handle_info({'EXIT', Port, Reason}, State = #{port := Port}) ->
110
:-(
{stop, {port_exit, Reason}, State};
111 handle_info(_Info, State) ->
112
:-(
{noreply, State}.
113
114 terminate(_Reason, #{port := Port}) ->
115
:-(
catch port_close(Port),
116
:-(
ok.
Line Hits Source