auser-poolparty 0.2.66 → 0.2.67
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Manifest.txt +83 -41
- data/PostInstall.txt +2 -2
- data/README.txt +1 -2
- data/Rakefile +14 -1
- data/bin/cloud-start +11 -10
- data/bin/{pool-spec → pool-generate} +0 -0
- data/bin/pool-init +3 -3
- data/bin/pool-start +8 -7
- data/bin/server-update-hosts +1 -1
- data/lib/erlang/messenger/ebin/pm_client_rel-0.1.rel +1 -1
- data/lib/erlang/messenger/ebin/pm_master_rel-0.1.rel +1 -1
- data/lib/erlang/messenger/ebin/pm_node_rel-0.1.rel +1 -1
- data/lib/erlang/messenger/include/defines.hrl +7 -3
- data/lib/erlang/messenger/lib/eunit/.svn/all-wcprops +53 -0
- data/lib/erlang/messenger/lib/eunit/.svn/entries +140 -0
- data/lib/erlang/messenger/lib/eunit/.svn/format +1 -0
- data/lib/erlang/messenger/lib/eunit/.svn/prop-base/NOTES.svn-base +5 -0
- data/lib/erlang/messenger/lib/eunit/.svn/text-base/AUTHORS.svn-base +2 -0
- data/lib/erlang/messenger/lib/eunit/.svn/text-base/CHANGELOG.svn-base +14 -0
- data/lib/erlang/messenger/lib/eunit/.svn/text-base/COPYING.svn-base +504 -0
- data/lib/erlang/messenger/lib/eunit/.svn/text-base/NOTES.svn-base +276 -0
- data/lib/erlang/messenger/lib/eunit/.svn/text-base/README.svn-base +3 -0
- data/lib/erlang/messenger/lib/eunit/.svn/text-base/sys.config.svn-base +9 -0
- data/lib/erlang/messenger/lib/eunit/.svn/text-base/vsn.mk.svn-base +1 -0
- data/lib/erlang/messenger/lib/eunit/doc/.svn/all-wcprops +59 -0
- data/lib/erlang/messenger/lib/eunit/doc/.svn/entries +142 -0
- data/lib/erlang/messenger/lib/eunit/doc/.svn/format +1 -0
- data/lib/erlang/messenger/lib/eunit/doc/.svn/prop-base/erlang.png.svn-base +5 -0
- data/lib/erlang/messenger/lib/eunit/doc/.svn/prop-base/eunit.html.svn-base +5 -0
- data/lib/erlang/messenger/lib/eunit/doc/.svn/prop-base/index.html.svn-base +5 -0
- data/lib/erlang/messenger/lib/eunit/doc/.svn/prop-base/modules-frame.html.svn-base +5 -0
- data/lib/erlang/messenger/lib/eunit/doc/.svn/prop-base/overview-summary.html.svn-base +5 -0
- data/lib/erlang/messenger/lib/eunit/doc/.svn/prop-base/packages-frame.html.svn-base +5 -0
- data/lib/erlang/messenger/lib/eunit/doc/.svn/text-base/edoc-info.svn-base +3 -0
- data/lib/erlang/messenger/lib/eunit/doc/.svn/text-base/erlang.png.svn-base +0 -0
- data/lib/erlang/messenger/lib/eunit/doc/.svn/text-base/eunit.html.svn-base +172 -0
- data/lib/erlang/messenger/lib/eunit/doc/.svn/text-base/index.html.svn-base +17 -0
- data/lib/erlang/messenger/lib/eunit/doc/.svn/text-base/modules-frame.html.svn-base +12 -0
- data/lib/erlang/messenger/lib/eunit/doc/.svn/text-base/overview-summary.html.svn-base +984 -0
- data/lib/erlang/messenger/lib/eunit/doc/.svn/text-base/overview.edoc.svn-base +980 -0
- data/lib/erlang/messenger/lib/eunit/doc/.svn/text-base/packages-frame.html.svn-base +11 -0
- data/lib/erlang/messenger/lib/eunit/doc/.svn/text-base/stylesheet.css.svn-base +55 -0
- data/lib/erlang/messenger/lib/eunit/ebin/.svn/all-wcprops +5 -0
- data/lib/erlang/messenger/lib/eunit/ebin/.svn/dir-prop-base +8 -0
- data/lib/erlang/messenger/lib/eunit/ebin/.svn/entries +28 -0
- data/lib/erlang/messenger/lib/eunit/ebin/.svn/format +1 -0
- data/lib/erlang/messenger/lib/eunit/examples/.svn/all-wcprops +23 -0
- data/lib/erlang/messenger/lib/eunit/examples/.svn/entries +66 -0
- data/lib/erlang/messenger/lib/eunit/examples/.svn/format +1 -0
- data/lib/erlang/messenger/lib/eunit/examples/.svn/prop-base/eunit_examples.erl.svn-base +5 -0
- data/lib/erlang/messenger/lib/eunit/examples/.svn/prop-base/fib.erl.svn-base +5 -0
- data/lib/erlang/messenger/lib/eunit/examples/.svn/text-base/eunit_examples.erl.svn-base +339 -0
- data/lib/erlang/messenger/lib/eunit/examples/.svn/text-base/fib.erl.svn-base +19 -0
- data/lib/erlang/messenger/lib/eunit/examples/.svn/text-base/tests.txt.svn-base +1 -0
- data/lib/erlang/messenger/lib/eunit/include/.svn/all-wcprops +11 -0
- data/lib/erlang/messenger/lib/eunit/include/.svn/entries +41 -0
- data/lib/erlang/messenger/lib/eunit/include/.svn/format +1 -0
- data/lib/erlang/messenger/lib/eunit/include/.svn/prop-base/eunit.hrl.svn-base +5 -0
- data/lib/erlang/messenger/lib/eunit/include/.svn/text-base/eunit.hrl.svn-base +313 -0
- data/lib/erlang/messenger/lib/eunit/src/.svn/all-wcprops +113 -0
- data/lib/erlang/messenger/lib/eunit/src/.svn/entries +259 -0
- data/lib/erlang/messenger/lib/eunit/src/.svn/format +1 -0
- data/lib/erlang/messenger/lib/eunit/src/.svn/prop-base/autoload.erl.svn-base +5 -0
- data/lib/erlang/messenger/lib/eunit/src/.svn/prop-base/code_monitor.erl.svn-base +5 -0
- data/lib/erlang/messenger/lib/eunit/src/.svn/prop-base/eunit.erl.svn-base +5 -0
- data/lib/erlang/messenger/lib/eunit/src/.svn/prop-base/eunit_autoexport.erl.svn-base +5 -0
- data/lib/erlang/messenger/lib/eunit/src/.svn/prop-base/eunit_data.erl.svn-base +5 -0
- data/lib/erlang/messenger/lib/eunit/src/.svn/prop-base/eunit_internal.hrl.svn-base +5 -0
- data/lib/erlang/messenger/lib/eunit/src/.svn/prop-base/eunit_lib.erl.svn-base +5 -0
- data/lib/erlang/messenger/lib/eunit/src/.svn/prop-base/eunit_proc.erl.svn-base +5 -0
- data/lib/erlang/messenger/lib/eunit/src/.svn/prop-base/eunit_serial.erl.svn-base +5 -0
- data/lib/erlang/messenger/lib/eunit/src/.svn/prop-base/eunit_server.erl.svn-base +5 -0
- data/lib/erlang/messenger/lib/eunit/src/.svn/prop-base/eunit_striptests.erl.svn-base +5 -0
- data/lib/erlang/messenger/lib/eunit/src/.svn/prop-base/eunit_test.erl.svn-base +5 -0
- data/lib/erlang/messenger/lib/eunit/src/.svn/prop-base/eunit_tests.erl.svn-base +5 -0
- data/lib/erlang/messenger/lib/eunit/src/.svn/prop-base/eunit_tty.erl.svn-base +5 -0
- data/lib/erlang/messenger/lib/eunit/src/.svn/prop-base/file_monitor.erl.svn-base +5 -0
- data/lib/erlang/messenger/lib/eunit/src/.svn/text-base/autoload.erl.svn-base +388 -0
- data/lib/erlang/messenger/lib/eunit/src/.svn/text-base/code_monitor.erl.svn-base +243 -0
- data/lib/erlang/messenger/lib/eunit/src/.svn/text-base/eunit.app.src.svn-base +21 -0
- data/lib/erlang/messenger/lib/eunit/src/.svn/text-base/eunit.appup.src.svn-base +1 -0
- data/lib/erlang/messenger/lib/eunit/src/.svn/text-base/eunit.erl.svn-base +196 -0
- data/lib/erlang/messenger/lib/eunit/src/.svn/text-base/eunit_autoexport.erl.svn-base +102 -0
- data/lib/erlang/messenger/lib/eunit/src/.svn/text-base/eunit_data.erl.svn-base +798 -0
- data/lib/erlang/messenger/lib/eunit/src/.svn/text-base/eunit_internal.hrl.svn-base +48 -0
- data/lib/erlang/messenger/lib/eunit/src/.svn/text-base/eunit_lib.erl.svn-base +682 -0
- data/lib/erlang/messenger/lib/eunit/src/.svn/text-base/eunit_proc.erl.svn-base +552 -0
- data/lib/erlang/messenger/lib/eunit/src/.svn/text-base/eunit_serial.erl.svn-base +157 -0
- data/lib/erlang/messenger/lib/eunit/src/.svn/text-base/eunit_server.erl.svn-base +340 -0
- data/lib/erlang/messenger/lib/eunit/src/.svn/text-base/eunit_striptests.erl.svn-base +64 -0
- data/lib/erlang/messenger/lib/eunit/src/.svn/text-base/eunit_test.erl.svn-base +334 -0
- data/lib/erlang/messenger/lib/eunit/src/.svn/text-base/eunit_tests.erl.svn-base +45 -0
- data/lib/erlang/messenger/lib/eunit/src/.svn/text-base/eunit_tty.erl.svn-base +272 -0
- data/lib/erlang/messenger/lib/eunit/src/.svn/text-base/file_monitor.erl.svn-base +409 -0
- data/lib/erlang/messenger/pm_client_rel-0.1.boot +0 -0
- data/lib/erlang/messenger/pm_client_rel-0.1.script +77 -85
- data/lib/erlang/messenger/pm_master_rel-0.1.boot +0 -0
- data/lib/erlang/messenger/pm_master_rel-0.1.script +78 -85
- data/lib/erlang/messenger/pm_node_rel-0.1.boot +0 -0
- data/lib/erlang/messenger/pm_node_rel-0.1.script +77 -86
- data/lib/erlang/messenger/src/pm_node.erl +46 -9
- data/lib/erlang/messenger/src/utils.erl +7 -1
- data/lib/poolparty.rb +17 -23
- data/lib/poolparty/base_packages/poolparty.rb +1 -1
- data/lib/poolparty/core/string.rb +11 -2
- data/lib/poolparty/helpers/binary.rb +31 -0
- data/lib/poolparty/helpers/console.rb +25 -16
- data/lib/poolparty/helpers/nice_printer.rb +36 -0
- data/lib/poolparty/helpers/optioner.rb +8 -0
- data/lib/poolparty/helpers/provisioner_base.rb +7 -5
- data/lib/poolparty/helpers/provisioners/master.rb +1 -1
- data/lib/poolparty/helpers/provisioners/slave.rb +2 -1
- data/lib/poolparty/modules/cloud_resourcer.rb +1 -1
- data/lib/poolparty/modules/file_writer.rb +12 -1
- data/lib/poolparty/modules/resourcing_dsl.rb +2 -1
- data/lib/poolparty/monitors/base_monitor.rb +3 -0
- data/lib/poolparty/net/remoter.rb +13 -11
- data/lib/poolparty/pool/base.rb +25 -13
- data/lib/poolparty/pool/cloud.rb +32 -10
- data/lib/poolparty/pool/custom_resource.rb +16 -7
- data/lib/poolparty/pool/plugin_model.rb +2 -2
- data/lib/poolparty/pool/pool.rb +2 -2
- data/lib/poolparty/pool/resource.rb +25 -7
- data/lib/poolparty/pool/resources/class_package.rb +3 -2
- data/lib/poolparty/pool/resources/exec.rb +1 -1
- data/lib/poolparty/pool/resources/variable.rb +4 -0
- data/lib/poolparty/version.rb +1 -1
- data/poolparty.gemspec +13 -11
- data/spec/poolparty/core/hash_spec.rb +1 -1
- data/spec/poolparty/core/time_spec.rb +1 -1
- data/spec/poolparty/net/remote_spec.rb +1 -1
- data/spec/poolparty/pool/base_spec.rb +25 -20
- data/spec/poolparty/pool/cloud_spec.rb +50 -3
- data/spec/poolparty/pool/plugin_spec.rb +1 -0
- data/spec/poolparty/pool/resource_spec.rb +4 -3
- data/spec/poolparty/spec_helper.rb +3 -4
- data/tasks/deployment.rake +15 -3
- data/website/index.html +2 -2
- metadata +88 -46
- data/lib/erlang/messenger/Makefile +0 -15
- data/lib/erlang/messenger/lib/eunit/Makefile +0 -28
- data/lib/erlang/messenger/lib/eunit/ebin/autoload.beam +0 -0
- data/lib/erlang/messenger/lib/eunit/ebin/code_monitor.beam +0 -0
- data/lib/erlang/messenger/lib/eunit/ebin/eunit.beam +0 -0
- data/lib/erlang/messenger/lib/eunit/ebin/eunit_autoexport.beam +0 -0
- data/lib/erlang/messenger/lib/eunit/ebin/eunit_data.beam +0 -0
- data/lib/erlang/messenger/lib/eunit/ebin/eunit_lib.beam +0 -0
- data/lib/erlang/messenger/lib/eunit/ebin/eunit_proc.beam +0 -0
- data/lib/erlang/messenger/lib/eunit/ebin/eunit_serial.beam +0 -0
- data/lib/erlang/messenger/lib/eunit/ebin/eunit_server.beam +0 -0
- data/lib/erlang/messenger/lib/eunit/ebin/eunit_striptests.beam +0 -0
- data/lib/erlang/messenger/lib/eunit/ebin/eunit_test.beam +0 -0
- data/lib/erlang/messenger/lib/eunit/ebin/eunit_tests.beam +0 -0
- data/lib/erlang/messenger/lib/eunit/ebin/eunit_tty.beam +0 -0
- data/lib/erlang/messenger/lib/eunit/ebin/file_monitor.beam +0 -0
- data/lib/erlang/messenger/lib/eunit/src/Makefile +0 -46
- data/lib/poolparty/config/allowed_commands.yml +0 -1
- data/lib/poolparty/plugins/git.rb +0 -45
- data/spec/poolparty/plugins/git_spec.rb +0 -40
@@ -0,0 +1,157 @@
|
|
1
|
+
%% This library is free software; you can redistribute it and/or modify
|
2
|
+
%% it under the terms of the GNU Lesser General Public License as
|
3
|
+
%% published by the Free Software Foundation; either version 2 of the
|
4
|
+
%% License, or (at your option) any later version.
|
5
|
+
%%
|
6
|
+
%% This library is distributed in the hope that it will be useful, but
|
7
|
+
%% WITHOUT ANY WARRANTY; without even the implied warranty of
|
8
|
+
%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
9
|
+
%% Lesser General Public License for more details.
|
10
|
+
%%
|
11
|
+
%% You should have received a copy of the GNU Lesser General Public
|
12
|
+
%% License along with this library; if not, write to the Free Software
|
13
|
+
%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
14
|
+
%% USA
|
15
|
+
%%
|
16
|
+
%% $Id$
|
17
|
+
%%
|
18
|
+
%% @author Richard Carlsson <richardc@it.uu.se>
|
19
|
+
%% @copyright 2006 Richard Carlsson
|
20
|
+
%% @private
|
21
|
+
%% @see eunit
|
22
|
+
%% @doc Event serializing process which works as an adapter and
|
23
|
+
%% multiplexer for "supervisor" processes
|
24
|
+
|
25
|
+
-module(eunit_serial).
|
26
|
+
|
27
|
+
-include("eunit.hrl").
|
28
|
+
-include("eunit_internal.hrl").
|
29
|
+
|
30
|
+
-export([start/1]).
|
31
|
+
|
32
|
+
%% Notes:
|
33
|
+
%% * Due to concurrency, there are no guarantees that we will receive
|
34
|
+
%% all status messages for the items within a group before we receive
|
35
|
+
%% the 'end' message of the group itself.
|
36
|
+
%%
|
37
|
+
%% * A cancelling event may arrive at any time, and may concern items we
|
38
|
+
%% are not yet expecting (if tests are executed in parallel), or may
|
39
|
+
%% concern not only the current item but possibly a group ancestor of
|
40
|
+
%% the current item (as in the case of a group timeout).
|
41
|
+
%%
|
42
|
+
%% * It is not possible to use selective receive to extract only those
|
43
|
+
%% cancelling messages that affect the current item and its parents;
|
44
|
+
%% basically, because we cannot have a dynamically computed prefix as a
|
45
|
+
%% pattern in a receive. Hence, we must extract each cancelling event as
|
46
|
+
%% it arrives and keep track of them separately.
|
47
|
+
%%
|
48
|
+
%% * Before we wait for a new item, we must check whether it (and thus
|
49
|
+
%% also all its subitems, if any) is already cancelled.
|
50
|
+
%%
|
51
|
+
%% * When a new cancelling event arrives, we must either store it for
|
52
|
+
%% future use, and/or cancel the current item and possibly one or more
|
53
|
+
%% of its parent groups.
|
54
|
+
|
55
|
+
-record(state, {listeners,
|
56
|
+
cancelled = eunit_lib:trie_new(),
|
57
|
+
messages = dict:new()}).
|
58
|
+
|
59
|
+
start(Pids) ->
|
60
|
+
spawn(fun () -> serializer(Pids) end).
|
61
|
+
|
62
|
+
serializer(Pids) ->
|
63
|
+
St = #state{listeners = sets:from_list(Pids),
|
64
|
+
cancelled = eunit_lib:trie_new(),
|
65
|
+
messages = dict:new()},
|
66
|
+
item([], none, none, St),
|
67
|
+
exit(normal).
|
68
|
+
|
69
|
+
item(Id, ParentId, N0, St0) ->
|
70
|
+
case wait(Id, 'begin', ParentId, N0, St0) of
|
71
|
+
{none, St1} ->
|
72
|
+
{true, St1};
|
73
|
+
{{cancel, Done, undefined}, St1} ->
|
74
|
+
{Done, cast({status, Id, {cancel, undefined}}, St1)};
|
75
|
+
{{cancel, Done, Msg}, St1} ->
|
76
|
+
{Done, cast(Msg, St1)};
|
77
|
+
{{ok, Msg}, St1} ->
|
78
|
+
%%?debugVal({got_begin, Id, Msg}),
|
79
|
+
cast(Msg, St1),
|
80
|
+
St2 = case Msg of
|
81
|
+
{status, _, {progress, 'begin', group}} ->
|
82
|
+
items(Id, 0, St1);
|
83
|
+
_ -> St1
|
84
|
+
end,
|
85
|
+
case wait(Id, 'end', ParentId, N0, St2) of
|
86
|
+
{{cancel, Done, undefined}, St3} ->
|
87
|
+
{Done, cast({status, Id, {cancel, undefined}}, St3)};
|
88
|
+
{{cancel, Done, Msg1}, St3} ->
|
89
|
+
{Done, cast(Msg1, St3)};
|
90
|
+
{{ok, Msg1}, St3} ->
|
91
|
+
%%?debugVal({got_end, Id, Msg1}),
|
92
|
+
{false, cast(Msg1, St3)}
|
93
|
+
end
|
94
|
+
end.
|
95
|
+
|
96
|
+
items(ParentId, N0, St) ->
|
97
|
+
N = N0 + 1,
|
98
|
+
case item(ParentId ++ [N], ParentId, N0, St) of
|
99
|
+
{false, St1} ->
|
100
|
+
items(ParentId, N, St1);
|
101
|
+
{true, St1} ->
|
102
|
+
St1
|
103
|
+
end.
|
104
|
+
|
105
|
+
cast(M, St) ->
|
106
|
+
sets:fold(fun (L, M) -> L ! M end, M, St#state.listeners),
|
107
|
+
St.
|
108
|
+
|
109
|
+
wait(Id, Type, ParentId, N0, St) ->
|
110
|
+
%%?debugVal({wait, Id, Type}),
|
111
|
+
case check_cancelled(Id, St) of
|
112
|
+
no ->
|
113
|
+
case recall(Id, St) of
|
114
|
+
undefined ->
|
115
|
+
wait_1(Id, Type, ParentId, N0, St);
|
116
|
+
Msg ->
|
117
|
+
{{ok, Msg}, forget(Id, St)}
|
118
|
+
end;
|
119
|
+
Why ->
|
120
|
+
%%?debugVal({cancelled, Why, Id, ParentId}),
|
121
|
+
Done = (Why =:= prefix),
|
122
|
+
{{cancel, Done, recall(Id, St)}, forget(Id, St)}
|
123
|
+
end.
|
124
|
+
|
125
|
+
wait_1(Id, Type, ParentId, N0, St) ->
|
126
|
+
receive
|
127
|
+
{status, Id, {progress, Type, _}}=Msg ->
|
128
|
+
%%?debugVal({Type, ParentId, Id}),
|
129
|
+
{{ok, Msg}, St};
|
130
|
+
{status,ParentId,{progress,'end',{N0,_,_}}}=Msg ->
|
131
|
+
%%?debugVal({end_group, ParentId, Id}),
|
132
|
+
{none, remember(ParentId, Msg, St)};
|
133
|
+
{status, SomeId, {cancel, _Cause}}=Msg ->
|
134
|
+
%%?debugVal({got_cancel, SomeId, ParentId, Id}),
|
135
|
+
St1 = set_cancelled(SomeId, Msg, St),
|
136
|
+
wait(Id, Type, ParentId, N0, St1)
|
137
|
+
end.
|
138
|
+
|
139
|
+
set_cancelled(Id, Msg, St0) ->
|
140
|
+
St = remember(Id, Msg, St0),
|
141
|
+
St#state{cancelled = eunit_lib:trie_store(Id, St0#state.cancelled)}.
|
142
|
+
|
143
|
+
check_cancelled(Id, St) ->
|
144
|
+
eunit_lib:trie_match(Id, St#state.cancelled).
|
145
|
+
|
146
|
+
remember(Id, Msg, St) ->
|
147
|
+
St#state{messages = dict:store(Id, Msg, St#state.messages)}.
|
148
|
+
|
149
|
+
forget(Id, St) ->
|
150
|
+
%% this is just to enable garbage collection of old messages
|
151
|
+
St#state{messages = dict:store(Id, undefined, St#state.messages)}.
|
152
|
+
|
153
|
+
recall(Id, St) ->
|
154
|
+
case dict:find(Id, St#state.messages) of
|
155
|
+
{ok, Msg} -> Msg;
|
156
|
+
error -> undefined
|
157
|
+
end.
|
@@ -0,0 +1,340 @@
|
|
1
|
+
%% This library is free software; you can redistribute it and/or modify
|
2
|
+
%% it under the terms of the GNU Lesser General Public License as
|
3
|
+
%% published by the Free Software Foundation; either version 2 of the
|
4
|
+
%% License, or (at your option) any later version.
|
5
|
+
%%
|
6
|
+
%% This library is distributed in the hope that it will be useful, but
|
7
|
+
%% WITHOUT ANY WARRANTY; without even the implied warranty of
|
8
|
+
%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
9
|
+
%% Lesser General Public License for more details.
|
10
|
+
%%
|
11
|
+
%% You should have received a copy of the GNU Lesser General Public
|
12
|
+
%% License along with this library; if not, write to the Free Software
|
13
|
+
%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
14
|
+
%% USA
|
15
|
+
%%
|
16
|
+
%% $Id:$
|
17
|
+
%%
|
18
|
+
%% @author Richard Carlsson <richardc@it.uu.se>
|
19
|
+
%% @copyright 2006 Richard Carlsson
|
20
|
+
%% @private
|
21
|
+
%% @see eunit
|
22
|
+
%% @doc EUnit server process
|
23
|
+
|
24
|
+
-module(eunit_server).
|
25
|
+
|
26
|
+
-export([start/1, stop/1, start_test/4, watch/3, watch_path/3,
|
27
|
+
watch_regexp/3]).
|
28
|
+
|
29
|
+
-export([main/1]). % private
|
30
|
+
|
31
|
+
-include("eunit.hrl").
|
32
|
+
-include("eunit_internal.hrl").
|
33
|
+
|
34
|
+
|
35
|
+
-define(AUTO_TIMEOUT, 60000). %% auto test time limit
|
36
|
+
|
37
|
+
%% TODO: pass options to server, such as default timeout?
|
38
|
+
|
39
|
+
start(Server) when is_atom(Server) ->
|
40
|
+
ensure_started(Server).
|
41
|
+
|
42
|
+
stop(Server) ->
|
43
|
+
command(Server, stop).
|
44
|
+
|
45
|
+
|
46
|
+
-record(job, {super, test, options}).
|
47
|
+
|
48
|
+
%% The `Super' process will receive a stream of status messages; see
|
49
|
+
%% eunit_proc:status_message/3 for details.
|
50
|
+
|
51
|
+
start_test(Server, Super, T, Options) ->
|
52
|
+
command(Server, {start, #job{super = Super,
|
53
|
+
test = T,
|
54
|
+
options = Options}}).
|
55
|
+
|
56
|
+
watch(Server, Module, Opts) when is_atom(Module) ->
|
57
|
+
command(Server, {watch, {module, Module}, Opts}).
|
58
|
+
|
59
|
+
watch_path(Server, Path, Opts) ->
|
60
|
+
command(Server, {watch, {path, filename:flatten(Path)}, Opts}).
|
61
|
+
|
62
|
+
watch_regexp(Server, Regex, Opts) ->
|
63
|
+
case regexp:parse(Regex) of
|
64
|
+
{ok, R} ->
|
65
|
+
command(Server, {watch, {regexp, R}, Opts});
|
66
|
+
{error, _}=Error ->
|
67
|
+
Error
|
68
|
+
end.
|
69
|
+
|
70
|
+
%% This makes sure the server is started before sending the command, and
|
71
|
+
%% returns {ok, Result} if the server accepted the command or {error,
|
72
|
+
%% server_down} if the server process crashes. If the server does not
|
73
|
+
%% reply, this function will wait until the server is killed.
|
74
|
+
|
75
|
+
command(Server, Cmd) ->
|
76
|
+
if is_atom(Server), Cmd /= stop -> ensure_started(Server);
|
77
|
+
true -> ok
|
78
|
+
end,
|
79
|
+
if is_pid(Server) -> command_1(Server, Cmd);
|
80
|
+
true ->
|
81
|
+
case whereis(Server) of
|
82
|
+
undefined -> {error, server_down};
|
83
|
+
Pid -> command_1(Pid, Cmd)
|
84
|
+
end
|
85
|
+
end.
|
86
|
+
|
87
|
+
command_1(Pid, Cmd) when is_pid(Pid) ->
|
88
|
+
Pid ! {command, self(), Cmd},
|
89
|
+
command_wait(Pid, 1000, undefined).
|
90
|
+
|
91
|
+
command_wait(Pid, Timeout, Monitor) ->
|
92
|
+
receive
|
93
|
+
{Pid, Result} -> Result;
|
94
|
+
{'DOWN', Monitor, process, Pid, _R} -> {error, server_down}
|
95
|
+
after Timeout ->
|
96
|
+
%% avoid creating a monitor unless some time has passed
|
97
|
+
command_wait(Pid, infinity, erlang:monitor(process, Pid))
|
98
|
+
end.
|
99
|
+
|
100
|
+
%% Starting the server
|
101
|
+
|
102
|
+
ensure_started(Name) ->
|
103
|
+
ensure_started(Name, 5).
|
104
|
+
|
105
|
+
ensure_started(Name, N) when N > 0 ->
|
106
|
+
case whereis(Name) of
|
107
|
+
undefined ->
|
108
|
+
Parent = self(),
|
109
|
+
Pid = spawn(fun () -> server_start(Name, Parent) end),
|
110
|
+
receive
|
111
|
+
{Pid, ok} ->
|
112
|
+
Pid;
|
113
|
+
{Pid, error} ->
|
114
|
+
receive after 200 -> ensure_started(N - 1) end
|
115
|
+
end;
|
116
|
+
Pid ->
|
117
|
+
Pid
|
118
|
+
end;
|
119
|
+
ensure_started(_, _) ->
|
120
|
+
throw(no_server).
|
121
|
+
|
122
|
+
server_start(undefined = Name, Parent) ->
|
123
|
+
%% anonymous server
|
124
|
+
server_start_1(Name, Parent);
|
125
|
+
server_start(Name, Parent) ->
|
126
|
+
try register(Name, self()) of
|
127
|
+
true -> server_start_1(Name, Parent)
|
128
|
+
catch
|
129
|
+
_:_ ->
|
130
|
+
Parent ! {self(), error},
|
131
|
+
exit(error)
|
132
|
+
end.
|
133
|
+
|
134
|
+
server_start_1(Name, Parent) ->
|
135
|
+
Parent ! {self(), ok},
|
136
|
+
server_init(Name).
|
137
|
+
|
138
|
+
-record(state, {name,
|
139
|
+
stopped,
|
140
|
+
jobs,
|
141
|
+
queue,
|
142
|
+
auto_test,
|
143
|
+
modules,
|
144
|
+
paths,
|
145
|
+
regexps}).
|
146
|
+
|
147
|
+
server_init(Name) ->
|
148
|
+
server(#state{name = Name,
|
149
|
+
stopped = false,
|
150
|
+
jobs = dict:new(),
|
151
|
+
queue = queue:new(),
|
152
|
+
auto_test = queue:new(),
|
153
|
+
modules = sets:new(),
|
154
|
+
paths = sets:new(),
|
155
|
+
regexps = sets:new()}).
|
156
|
+
|
157
|
+
server(St) ->
|
158
|
+
server_check_exit(St),
|
159
|
+
?MODULE:main(St).
|
160
|
+
|
161
|
+
%% @private
|
162
|
+
main(St) ->
|
163
|
+
receive
|
164
|
+
{done, auto_test, _Pid} ->
|
165
|
+
server(auto_test_done(St));
|
166
|
+
{done, Reference, _Pid} ->
|
167
|
+
server(handle_done(Reference, St));
|
168
|
+
{command, From, _Cmd} when St#state.stopped ->
|
169
|
+
From ! {self(), stopped};
|
170
|
+
{command, From, Cmd} ->
|
171
|
+
server_command(From, Cmd, St);
|
172
|
+
{code_monitor, {loaded, M, _Time}} ->
|
173
|
+
case is_watched(M, St) of
|
174
|
+
true ->
|
175
|
+
server(new_auto_test(self(), M, St));
|
176
|
+
false ->
|
177
|
+
server(St)
|
178
|
+
end
|
179
|
+
end.
|
180
|
+
|
181
|
+
server_check_exit(St) ->
|
182
|
+
case dict:size(St#state.jobs) of
|
183
|
+
0 when St#state.stopped -> exit(normal);
|
184
|
+
_ -> ok
|
185
|
+
end.
|
186
|
+
|
187
|
+
server_command(From, {start, Job}, St) ->
|
188
|
+
Reference = make_ref(),
|
189
|
+
St1 = case proplists:get_bool(enqueue, Job#job.options) of
|
190
|
+
true ->
|
191
|
+
enqueue(Job, From, Reference, St);
|
192
|
+
false ->
|
193
|
+
start_job(Job, From, Reference, St)
|
194
|
+
end,
|
195
|
+
server_command_reply(From, {ok, Reference}),
|
196
|
+
server(St1);
|
197
|
+
server_command(From, stop, St) ->
|
198
|
+
%% unregister the server name and let remaining jobs finish
|
199
|
+
server_command_reply(From, {error, stopped}),
|
200
|
+
catch unregister(St#state.name),
|
201
|
+
server(St#state{stopped = true});
|
202
|
+
server_command(From, {watch, Target, _Opts}, St) ->
|
203
|
+
%% the code watcher is only started on demand
|
204
|
+
code_monitor:monitor(self()),
|
205
|
+
%% TODO: propagate options to testing stage
|
206
|
+
St1 = add_watch(Target, St),
|
207
|
+
server_command_reply(From, ok),
|
208
|
+
server(St1);
|
209
|
+
server_command(From, {forget, Target}, St) ->
|
210
|
+
St1 = delete_watch(Target, St),
|
211
|
+
server_command_reply(From, ok),
|
212
|
+
server(St1);
|
213
|
+
server_command(From, Cmd, St) ->
|
214
|
+
server_command_reply(From, {error, {unknown_command, Cmd}}),
|
215
|
+
server(St).
|
216
|
+
|
217
|
+
server_command_reply(From, Result) ->
|
218
|
+
From ! {self(), Result}.
|
219
|
+
|
220
|
+
enqueue(Job, From, Reference, St) ->
|
221
|
+
case dict:size(St#state.jobs) of
|
222
|
+
0 ->
|
223
|
+
start_job(Job, From, Reference, St);
|
224
|
+
_ ->
|
225
|
+
St#state{queue = queue:in({Job, From, Reference},
|
226
|
+
St#state.queue)}
|
227
|
+
end.
|
228
|
+
|
229
|
+
dequeue(St) ->
|
230
|
+
case queue:out(St#state.queue) of
|
231
|
+
{empty, _} ->
|
232
|
+
St;
|
233
|
+
{{value, {Job, From, Reference}}, Queue} ->
|
234
|
+
start_job(Job, From, Reference, St#state{queue = Queue})
|
235
|
+
end.
|
236
|
+
|
237
|
+
start_job(Job, From, Reference, St) ->
|
238
|
+
From ! {start, Reference},
|
239
|
+
%% The default is to run tests in order unless otherwise specified
|
240
|
+
Order = proplists:get_value(order, Job#job.options, inorder),
|
241
|
+
eunit_proc:start(Job#job.test, Order, Job#job.super, Reference),
|
242
|
+
St#state{jobs = dict:store(Reference, From, St#state.jobs)}.
|
243
|
+
|
244
|
+
handle_done(Reference, St) ->
|
245
|
+
case dict:find(Reference, St#state.jobs) of
|
246
|
+
{ok, From} ->
|
247
|
+
From ! {done, Reference},
|
248
|
+
dequeue(St#state{jobs = dict:erase(Reference,
|
249
|
+
St#state.jobs)});
|
250
|
+
error ->
|
251
|
+
St
|
252
|
+
end.
|
253
|
+
|
254
|
+
%% Adding and removing watched modules or paths
|
255
|
+
|
256
|
+
add_watch({module, M}, St) ->
|
257
|
+
St#state{modules = sets:add_element(M, St#state.modules)};
|
258
|
+
add_watch({path, P}, St) ->
|
259
|
+
St#state{paths = sets:add_element(P, St#state.paths)};
|
260
|
+
add_watch({regexp, R}, St) ->
|
261
|
+
St#state{regexps = sets:add_element(R, St#state.regexps)}.
|
262
|
+
|
263
|
+
delete_watch({module, M}, St) ->
|
264
|
+
St#state{modules = sets:del_element(M, St#state.modules)};
|
265
|
+
delete_watch({path, P}, St) ->
|
266
|
+
St#state{paths = sets:del_element(P, St#state.paths)};
|
267
|
+
delete_watch({regexp, R}, St) ->
|
268
|
+
St#state{regexps = sets:del_element(R, St#state.regexps)}.
|
269
|
+
|
270
|
+
%% Checking if a module is being watched
|
271
|
+
|
272
|
+
is_watched(M, St) when is_atom(M) ->
|
273
|
+
sets:is_element(M, St#state.modules) orelse
|
274
|
+
is_watched(code:which(M), St);
|
275
|
+
is_watched(Path, St) ->
|
276
|
+
sets:is_element(filename:dirname(Path), St#state.paths) orelse
|
277
|
+
match_any(sets:to_list(St#state.regexps), Path).
|
278
|
+
|
279
|
+
match_any([R | Rs], Str) ->
|
280
|
+
case regexp:first_match(Str, R) of
|
281
|
+
{match, _, _} -> true;
|
282
|
+
_ -> match_any(Rs, Str)
|
283
|
+
end;
|
284
|
+
match_any([], _Str) -> false.
|
285
|
+
|
286
|
+
%% Running automatic tests when a watched module is loaded.
|
287
|
+
%% Uses a queue in order to avoid overlapping output when several
|
288
|
+
%% watched modules are loaded simultaneously. (The currently running
|
289
|
+
%% automatic test is kept in the queue until it is done. An empty queue
|
290
|
+
%% means that no automatic test is running.)
|
291
|
+
|
292
|
+
new_auto_test(Server, M, St) ->
|
293
|
+
case queue:is_empty(St#state.auto_test) of
|
294
|
+
true ->
|
295
|
+
start_auto_test(Server, M);
|
296
|
+
false ->
|
297
|
+
ok
|
298
|
+
end,
|
299
|
+
St#state{auto_test = queue:in({Server, M}, St#state.auto_test)}.
|
300
|
+
|
301
|
+
auto_test_done(St) ->
|
302
|
+
%% remove finished test from queue before checking for more
|
303
|
+
{_, Queue} = queue:out(St#state.auto_test),
|
304
|
+
case queue:out(Queue) of
|
305
|
+
{{value, {Server, M}}, _} ->
|
306
|
+
%% this is just lookahead - the item is not removed
|
307
|
+
start_auto_test(Server, M);
|
308
|
+
{empty, _} ->
|
309
|
+
ok
|
310
|
+
end,
|
311
|
+
St#state{auto_test = Queue}.
|
312
|
+
|
313
|
+
start_auto_test(Server, M) ->
|
314
|
+
spawn(fun () -> auto_super(Server, M) end).
|
315
|
+
|
316
|
+
auto_super(Server, M) ->
|
317
|
+
process_flag(trap_exit, true),
|
318
|
+
%% Give the user a short delay before any output is produced
|
319
|
+
receive after 333 -> ok end,
|
320
|
+
%% Make sure output is sent to console on server node
|
321
|
+
group_leader(whereis(user), self()),
|
322
|
+
Pid = spawn_link(fun () -> auto_proc(Server, M) end),
|
323
|
+
receive
|
324
|
+
{'EXIT', Pid, _} ->
|
325
|
+
ok
|
326
|
+
after ?AUTO_TIMEOUT ->
|
327
|
+
exit(Pid, kill),
|
328
|
+
io:put_chars("\n== EUnit: automatic test was aborted ==\n"),
|
329
|
+
io:put_chars("\n> ")
|
330
|
+
end,
|
331
|
+
Server ! {done, auto_test, self()}.
|
332
|
+
|
333
|
+
auto_proc(Server, M) ->
|
334
|
+
%% Make the output start on a new line instead of on the same line
|
335
|
+
%% as the current shell prompt.
|
336
|
+
io:fwrite("\n== EUnit: testing module ~w ==\n", [M]),
|
337
|
+
eunit:test(Server, M, [enqueue]),
|
338
|
+
%% Make sure to print a dummy prompt at the end of the output, most
|
339
|
+
%% of all so that the Emacs mode realizes that input is active.
|
340
|
+
io:put_chars("\n-> ").
|