/home/runner/work/chrme/chrme/_build/test/cover/aggregate/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 4 Name = maps:get(name, Options),
36 4 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 17 Options = gen_server:call({global, Name}, get_options),
42 17 Host = <<"localhost">>,
43 17 Port = maps:get(remote_port, Options),
44 17 case chrme_http_apic:version(Host, Port) of
45 {ok, _} ->
46 4 ok;
47 _Error ->
48 13 timer:sleep(100),
49 13 await_start(Name)
50 end.
51
52 -spec stop(name()) -> ok.
53 stop(Name) ->
54 4 gen_server:stop({global, Name}).
55
56 -spec init(options()) -> {ok, state()}.
57 init(Options0) ->
58 4 Options = normalize_options(Options0),
59 4 PathBin = maps:get(executable, Options),
60 4 Path = binary_to_list(PathBin),
61 4 RemotePort = maps:get(remote_port, Options),
62 4 RemotePortStr = integer_to_list(RemotePort),
63 4 DataDirBin = maps:get(user_data_dir, Options),
64 4 DataDir = binary_to_list(DataDirBin),
65 4 Headless = maps:get(headless, Options),
66 4 ExtraArgsBin = maps:get(extra_args, Options),
67 4 ExtraArgs = [binary_to_list(B) || B <- ExtraArgsBin],
68 4 BaseArgs = [
69 "--remote-debugging-port=" ++ RemotePortStr,
70 "--user-data-dir=" ++ DataDir
71 ],
72 4 HeadlessArgs = case Headless of
73 4 true -> ["--headless"];
74
:-(
false -> []
75 end,
76 4 CmdArgs = BaseArgs ++ HeadlessArgs ++ ExtraArgs,
77 4 process_flag(trap_exit, true),
78 4 Port = open_port({spawn_executable, Path}, [
79 binary,
80 exit_status,
81 use_stdio,
82 stderr_to_stdout,
83 {args, CmdArgs}
84 ]),
85 4 {ok, #{port => Port, opts => Options}}.
86
87 % Internal: merge user options with defaults
88 -spec normalize_options(map()) -> options().
89 normalize_options(Options) ->
90 4 #{
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 17 {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 54 {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 4 catch port_close(Port),
116 4 ok.
Line Hits Source