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.
Files changed (159) hide show
  1. data/Manifest.txt +83 -41
  2. data/PostInstall.txt +2 -2
  3. data/README.txt +1 -2
  4. data/Rakefile +14 -1
  5. data/bin/cloud-start +11 -10
  6. data/bin/{pool-spec → pool-generate} +0 -0
  7. data/bin/pool-init +3 -3
  8. data/bin/pool-start +8 -7
  9. data/bin/server-update-hosts +1 -1
  10. data/lib/erlang/messenger/ebin/pm_client_rel-0.1.rel +1 -1
  11. data/lib/erlang/messenger/ebin/pm_master_rel-0.1.rel +1 -1
  12. data/lib/erlang/messenger/ebin/pm_node_rel-0.1.rel +1 -1
  13. data/lib/erlang/messenger/include/defines.hrl +7 -3
  14. data/lib/erlang/messenger/lib/eunit/.svn/all-wcprops +53 -0
  15. data/lib/erlang/messenger/lib/eunit/.svn/entries +140 -0
  16. data/lib/erlang/messenger/lib/eunit/.svn/format +1 -0
  17. data/lib/erlang/messenger/lib/eunit/.svn/prop-base/NOTES.svn-base +5 -0
  18. data/lib/erlang/messenger/lib/eunit/.svn/text-base/AUTHORS.svn-base +2 -0
  19. data/lib/erlang/messenger/lib/eunit/.svn/text-base/CHANGELOG.svn-base +14 -0
  20. data/lib/erlang/messenger/lib/eunit/.svn/text-base/COPYING.svn-base +504 -0
  21. data/lib/erlang/messenger/lib/eunit/.svn/text-base/NOTES.svn-base +276 -0
  22. data/lib/erlang/messenger/lib/eunit/.svn/text-base/README.svn-base +3 -0
  23. data/lib/erlang/messenger/lib/eunit/.svn/text-base/sys.config.svn-base +9 -0
  24. data/lib/erlang/messenger/lib/eunit/.svn/text-base/vsn.mk.svn-base +1 -0
  25. data/lib/erlang/messenger/lib/eunit/doc/.svn/all-wcprops +59 -0
  26. data/lib/erlang/messenger/lib/eunit/doc/.svn/entries +142 -0
  27. data/lib/erlang/messenger/lib/eunit/doc/.svn/format +1 -0
  28. data/lib/erlang/messenger/lib/eunit/doc/.svn/prop-base/erlang.png.svn-base +5 -0
  29. data/lib/erlang/messenger/lib/eunit/doc/.svn/prop-base/eunit.html.svn-base +5 -0
  30. data/lib/erlang/messenger/lib/eunit/doc/.svn/prop-base/index.html.svn-base +5 -0
  31. data/lib/erlang/messenger/lib/eunit/doc/.svn/prop-base/modules-frame.html.svn-base +5 -0
  32. data/lib/erlang/messenger/lib/eunit/doc/.svn/prop-base/overview-summary.html.svn-base +5 -0
  33. data/lib/erlang/messenger/lib/eunit/doc/.svn/prop-base/packages-frame.html.svn-base +5 -0
  34. data/lib/erlang/messenger/lib/eunit/doc/.svn/text-base/edoc-info.svn-base +3 -0
  35. data/lib/erlang/messenger/lib/eunit/doc/.svn/text-base/erlang.png.svn-base +0 -0
  36. data/lib/erlang/messenger/lib/eunit/doc/.svn/text-base/eunit.html.svn-base +172 -0
  37. data/lib/erlang/messenger/lib/eunit/doc/.svn/text-base/index.html.svn-base +17 -0
  38. data/lib/erlang/messenger/lib/eunit/doc/.svn/text-base/modules-frame.html.svn-base +12 -0
  39. data/lib/erlang/messenger/lib/eunit/doc/.svn/text-base/overview-summary.html.svn-base +984 -0
  40. data/lib/erlang/messenger/lib/eunit/doc/.svn/text-base/overview.edoc.svn-base +980 -0
  41. data/lib/erlang/messenger/lib/eunit/doc/.svn/text-base/packages-frame.html.svn-base +11 -0
  42. data/lib/erlang/messenger/lib/eunit/doc/.svn/text-base/stylesheet.css.svn-base +55 -0
  43. data/lib/erlang/messenger/lib/eunit/ebin/.svn/all-wcprops +5 -0
  44. data/lib/erlang/messenger/lib/eunit/ebin/.svn/dir-prop-base +8 -0
  45. data/lib/erlang/messenger/lib/eunit/ebin/.svn/entries +28 -0
  46. data/lib/erlang/messenger/lib/eunit/ebin/.svn/format +1 -0
  47. data/lib/erlang/messenger/lib/eunit/examples/.svn/all-wcprops +23 -0
  48. data/lib/erlang/messenger/lib/eunit/examples/.svn/entries +66 -0
  49. data/lib/erlang/messenger/lib/eunit/examples/.svn/format +1 -0
  50. data/lib/erlang/messenger/lib/eunit/examples/.svn/prop-base/eunit_examples.erl.svn-base +5 -0
  51. data/lib/erlang/messenger/lib/eunit/examples/.svn/prop-base/fib.erl.svn-base +5 -0
  52. data/lib/erlang/messenger/lib/eunit/examples/.svn/text-base/eunit_examples.erl.svn-base +339 -0
  53. data/lib/erlang/messenger/lib/eunit/examples/.svn/text-base/fib.erl.svn-base +19 -0
  54. data/lib/erlang/messenger/lib/eunit/examples/.svn/text-base/tests.txt.svn-base +1 -0
  55. data/lib/erlang/messenger/lib/eunit/include/.svn/all-wcprops +11 -0
  56. data/lib/erlang/messenger/lib/eunit/include/.svn/entries +41 -0
  57. data/lib/erlang/messenger/lib/eunit/include/.svn/format +1 -0
  58. data/lib/erlang/messenger/lib/eunit/include/.svn/prop-base/eunit.hrl.svn-base +5 -0
  59. data/lib/erlang/messenger/lib/eunit/include/.svn/text-base/eunit.hrl.svn-base +313 -0
  60. data/lib/erlang/messenger/lib/eunit/src/.svn/all-wcprops +113 -0
  61. data/lib/erlang/messenger/lib/eunit/src/.svn/entries +259 -0
  62. data/lib/erlang/messenger/lib/eunit/src/.svn/format +1 -0
  63. data/lib/erlang/messenger/lib/eunit/src/.svn/prop-base/autoload.erl.svn-base +5 -0
  64. data/lib/erlang/messenger/lib/eunit/src/.svn/prop-base/code_monitor.erl.svn-base +5 -0
  65. data/lib/erlang/messenger/lib/eunit/src/.svn/prop-base/eunit.erl.svn-base +5 -0
  66. data/lib/erlang/messenger/lib/eunit/src/.svn/prop-base/eunit_autoexport.erl.svn-base +5 -0
  67. data/lib/erlang/messenger/lib/eunit/src/.svn/prop-base/eunit_data.erl.svn-base +5 -0
  68. data/lib/erlang/messenger/lib/eunit/src/.svn/prop-base/eunit_internal.hrl.svn-base +5 -0
  69. data/lib/erlang/messenger/lib/eunit/src/.svn/prop-base/eunit_lib.erl.svn-base +5 -0
  70. data/lib/erlang/messenger/lib/eunit/src/.svn/prop-base/eunit_proc.erl.svn-base +5 -0
  71. data/lib/erlang/messenger/lib/eunit/src/.svn/prop-base/eunit_serial.erl.svn-base +5 -0
  72. data/lib/erlang/messenger/lib/eunit/src/.svn/prop-base/eunit_server.erl.svn-base +5 -0
  73. data/lib/erlang/messenger/lib/eunit/src/.svn/prop-base/eunit_striptests.erl.svn-base +5 -0
  74. data/lib/erlang/messenger/lib/eunit/src/.svn/prop-base/eunit_test.erl.svn-base +5 -0
  75. data/lib/erlang/messenger/lib/eunit/src/.svn/prop-base/eunit_tests.erl.svn-base +5 -0
  76. data/lib/erlang/messenger/lib/eunit/src/.svn/prop-base/eunit_tty.erl.svn-base +5 -0
  77. data/lib/erlang/messenger/lib/eunit/src/.svn/prop-base/file_monitor.erl.svn-base +5 -0
  78. data/lib/erlang/messenger/lib/eunit/src/.svn/text-base/autoload.erl.svn-base +388 -0
  79. data/lib/erlang/messenger/lib/eunit/src/.svn/text-base/code_monitor.erl.svn-base +243 -0
  80. data/lib/erlang/messenger/lib/eunit/src/.svn/text-base/eunit.app.src.svn-base +21 -0
  81. data/lib/erlang/messenger/lib/eunit/src/.svn/text-base/eunit.appup.src.svn-base +1 -0
  82. data/lib/erlang/messenger/lib/eunit/src/.svn/text-base/eunit.erl.svn-base +196 -0
  83. data/lib/erlang/messenger/lib/eunit/src/.svn/text-base/eunit_autoexport.erl.svn-base +102 -0
  84. data/lib/erlang/messenger/lib/eunit/src/.svn/text-base/eunit_data.erl.svn-base +798 -0
  85. data/lib/erlang/messenger/lib/eunit/src/.svn/text-base/eunit_internal.hrl.svn-base +48 -0
  86. data/lib/erlang/messenger/lib/eunit/src/.svn/text-base/eunit_lib.erl.svn-base +682 -0
  87. data/lib/erlang/messenger/lib/eunit/src/.svn/text-base/eunit_proc.erl.svn-base +552 -0
  88. data/lib/erlang/messenger/lib/eunit/src/.svn/text-base/eunit_serial.erl.svn-base +157 -0
  89. data/lib/erlang/messenger/lib/eunit/src/.svn/text-base/eunit_server.erl.svn-base +340 -0
  90. data/lib/erlang/messenger/lib/eunit/src/.svn/text-base/eunit_striptests.erl.svn-base +64 -0
  91. data/lib/erlang/messenger/lib/eunit/src/.svn/text-base/eunit_test.erl.svn-base +334 -0
  92. data/lib/erlang/messenger/lib/eunit/src/.svn/text-base/eunit_tests.erl.svn-base +45 -0
  93. data/lib/erlang/messenger/lib/eunit/src/.svn/text-base/eunit_tty.erl.svn-base +272 -0
  94. data/lib/erlang/messenger/lib/eunit/src/.svn/text-base/file_monitor.erl.svn-base +409 -0
  95. data/lib/erlang/messenger/pm_client_rel-0.1.boot +0 -0
  96. data/lib/erlang/messenger/pm_client_rel-0.1.script +77 -85
  97. data/lib/erlang/messenger/pm_master_rel-0.1.boot +0 -0
  98. data/lib/erlang/messenger/pm_master_rel-0.1.script +78 -85
  99. data/lib/erlang/messenger/pm_node_rel-0.1.boot +0 -0
  100. data/lib/erlang/messenger/pm_node_rel-0.1.script +77 -86
  101. data/lib/erlang/messenger/src/pm_node.erl +46 -9
  102. data/lib/erlang/messenger/src/utils.erl +7 -1
  103. data/lib/poolparty.rb +17 -23
  104. data/lib/poolparty/base_packages/poolparty.rb +1 -1
  105. data/lib/poolparty/core/string.rb +11 -2
  106. data/lib/poolparty/helpers/binary.rb +31 -0
  107. data/lib/poolparty/helpers/console.rb +25 -16
  108. data/lib/poolparty/helpers/nice_printer.rb +36 -0
  109. data/lib/poolparty/helpers/optioner.rb +8 -0
  110. data/lib/poolparty/helpers/provisioner_base.rb +7 -5
  111. data/lib/poolparty/helpers/provisioners/master.rb +1 -1
  112. data/lib/poolparty/helpers/provisioners/slave.rb +2 -1
  113. data/lib/poolparty/modules/cloud_resourcer.rb +1 -1
  114. data/lib/poolparty/modules/file_writer.rb +12 -1
  115. data/lib/poolparty/modules/resourcing_dsl.rb +2 -1
  116. data/lib/poolparty/monitors/base_monitor.rb +3 -0
  117. data/lib/poolparty/net/remoter.rb +13 -11
  118. data/lib/poolparty/pool/base.rb +25 -13
  119. data/lib/poolparty/pool/cloud.rb +32 -10
  120. data/lib/poolparty/pool/custom_resource.rb +16 -7
  121. data/lib/poolparty/pool/plugin_model.rb +2 -2
  122. data/lib/poolparty/pool/pool.rb +2 -2
  123. data/lib/poolparty/pool/resource.rb +25 -7
  124. data/lib/poolparty/pool/resources/class_package.rb +3 -2
  125. data/lib/poolparty/pool/resources/exec.rb +1 -1
  126. data/lib/poolparty/pool/resources/variable.rb +4 -0
  127. data/lib/poolparty/version.rb +1 -1
  128. data/poolparty.gemspec +13 -11
  129. data/spec/poolparty/core/hash_spec.rb +1 -1
  130. data/spec/poolparty/core/time_spec.rb +1 -1
  131. data/spec/poolparty/net/remote_spec.rb +1 -1
  132. data/spec/poolparty/pool/base_spec.rb +25 -20
  133. data/spec/poolparty/pool/cloud_spec.rb +50 -3
  134. data/spec/poolparty/pool/plugin_spec.rb +1 -0
  135. data/spec/poolparty/pool/resource_spec.rb +4 -3
  136. data/spec/poolparty/spec_helper.rb +3 -4
  137. data/tasks/deployment.rake +15 -3
  138. data/website/index.html +2 -2
  139. metadata +88 -46
  140. data/lib/erlang/messenger/Makefile +0 -15
  141. data/lib/erlang/messenger/lib/eunit/Makefile +0 -28
  142. data/lib/erlang/messenger/lib/eunit/ebin/autoload.beam +0 -0
  143. data/lib/erlang/messenger/lib/eunit/ebin/code_monitor.beam +0 -0
  144. data/lib/erlang/messenger/lib/eunit/ebin/eunit.beam +0 -0
  145. data/lib/erlang/messenger/lib/eunit/ebin/eunit_autoexport.beam +0 -0
  146. data/lib/erlang/messenger/lib/eunit/ebin/eunit_data.beam +0 -0
  147. data/lib/erlang/messenger/lib/eunit/ebin/eunit_lib.beam +0 -0
  148. data/lib/erlang/messenger/lib/eunit/ebin/eunit_proc.beam +0 -0
  149. data/lib/erlang/messenger/lib/eunit/ebin/eunit_serial.beam +0 -0
  150. data/lib/erlang/messenger/lib/eunit/ebin/eunit_server.beam +0 -0
  151. data/lib/erlang/messenger/lib/eunit/ebin/eunit_striptests.beam +0 -0
  152. data/lib/erlang/messenger/lib/eunit/ebin/eunit_test.beam +0 -0
  153. data/lib/erlang/messenger/lib/eunit/ebin/eunit_tests.beam +0 -0
  154. data/lib/erlang/messenger/lib/eunit/ebin/eunit_tty.beam +0 -0
  155. data/lib/erlang/messenger/lib/eunit/ebin/file_monitor.beam +0 -0
  156. data/lib/erlang/messenger/lib/eunit/src/Makefile +0 -46
  157. data/lib/poolparty/config/allowed_commands.yml +0 -1
  158. data/lib/poolparty/plugins/git.rb +0 -45
  159. 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-> ").