thin_service 0.0.1
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/.gitignore +19 -0
- data/Gemfile +4 -0
- data/LICENSE +53 -0
- data/README.md +26 -0
- data/Rakefile +17 -0
- data/bin/thin_service +17 -0
- data/lib/thin_service.rb +15 -0
- data/lib/thin_service/command.rb +343 -0
- data/lib/thin_service/logger.rb +74 -0
- data/lib/thin_service/service_manager.rb +78 -0
- data/lib/thin_service/version.rb +3 -0
- data/resource/thin_service_wrapper.exe +0 -0
- data/spec/spec_helper.rb +7 -0
- data/spec/thin_service_spec.rb +43 -0
- data/src/ServiceFB/ServiceFB.bas +651 -0
- data/src/ServiceFB/ServiceFB.bi +109 -0
- data/src/ServiceFB/ServiceFB_Utils.bas +495 -0
- data/src/ServiceFB/ServiceFB_Utils.bi +70 -0
- data/src/ServiceFB/_internals.bi +50 -0
- data/src/ServiceFB/_utils_internals.bi +51 -0
- data/src/thin_service/_debug.bi +61 -0
- data/src/thin_service/boolean.bi +18 -0
- data/src/thin_service/console_process.bas +397 -0
- data/src/thin_service/console_process.bi +75 -0
- data/src/thin_service/thin_service.bas +199 -0
- data/src/thin_service/thin_service.bi +61 -0
- data/tasks/native_lib.rake +40 -0
- data/tasks/native_service.rake +43 -0
- data/tasks/tests.rake +55 -0
- data/tests/all_tests.bas +18 -0
- data/tests/fixtures/mock_process.bas +92 -0
- data/tests/test_console_process.bas +402 -0
- data/tests/test_helpers.bas +35 -0
- data/tests/test_helpers.bi +8 -0
- data/thin_service.gemspec +21 -0
- data/tools/freebasic.rb +359 -0
- metadata +106 -0
data/tests/all_tests.bas
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
'#--
|
2
|
+
'# Copyright (c) 2006-2007 Luis Lavena, Multimedia systems
|
3
|
+
'#
|
4
|
+
'# This source code is released under the MIT License.
|
5
|
+
'# See MIT-LICENSE file for details
|
6
|
+
'#++
|
7
|
+
|
8
|
+
#include once "testly.bi"
|
9
|
+
|
10
|
+
'# the code in this module runs after all
|
11
|
+
'# the other modules have "registered" their suites.
|
12
|
+
|
13
|
+
'# evaluate the result from run_tests() to
|
14
|
+
'# return a error to the OS or not.
|
15
|
+
if (run_tests() = false) then
|
16
|
+
end 1
|
17
|
+
end if
|
18
|
+
|
@@ -0,0 +1,92 @@
|
|
1
|
+
'#--
|
2
|
+
'# Copyright (c) 2006-2007 Luis Lavena, Multimedia systems
|
3
|
+
'#
|
4
|
+
'# This source code is released under the MIT License.
|
5
|
+
'# See MIT-LICENSE file for details
|
6
|
+
'#++
|
7
|
+
|
8
|
+
'# this program mock a common process that will:
|
9
|
+
'# output some text to stdout
|
10
|
+
'# output some error messages to stderr
|
11
|
+
'# will wait until Ctrl-C is hit (only if commandline contains "wait")
|
12
|
+
'# or drop an error if commandline contains "error"
|
13
|
+
|
14
|
+
#include once "crt.bi"
|
15
|
+
#include once "windows.bi"
|
16
|
+
|
17
|
+
dim shared as any ptr control_signal, control_mutex
|
18
|
+
dim shared flagged as byte
|
19
|
+
dim shared result as integer
|
20
|
+
|
21
|
+
function slow_console_handler(byval dwCtrlType as DWORD) as BOOL
|
22
|
+
dim result as BOOL
|
23
|
+
|
24
|
+
if (dwCtrlType = CTRL_C_EVENT) then
|
25
|
+
fprintf(stdout, !"out: CTRL-C received\r\n")
|
26
|
+
mutexlock(control_mutex)
|
27
|
+
result = 1
|
28
|
+
flagged = 1
|
29
|
+
condsignal(control_signal)
|
30
|
+
mutexunlock(control_mutex)
|
31
|
+
elseif (dwCtrlType = CTRL_BREAK_EVENT) then
|
32
|
+
fprintf(stdout, !"out: CTRL-BREAK received\r\n")
|
33
|
+
mutexlock(control_mutex)
|
34
|
+
result = 1
|
35
|
+
flagged = 2
|
36
|
+
condsignal(control_signal)
|
37
|
+
mutexunlock(control_mutex)
|
38
|
+
end if
|
39
|
+
|
40
|
+
return result
|
41
|
+
end function
|
42
|
+
|
43
|
+
sub wait_for(byval flag_level as integer)
|
44
|
+
flagged = 0
|
45
|
+
'# set handler
|
46
|
+
if (SetConsoleCtrlHandler(@slow_console_handler, 1) = 0) then
|
47
|
+
fprintf(stderr, !"err: cannot set console handler\r\n")
|
48
|
+
end if
|
49
|
+
fprintf(stdout, !"out: waiting for keyboard signal\r\n")
|
50
|
+
mutexlock(control_mutex)
|
51
|
+
do until (flagged = flag_level)
|
52
|
+
condwait(control_signal, control_mutex)
|
53
|
+
loop
|
54
|
+
mutexunlock(control_mutex)
|
55
|
+
fprintf(stdout, !"out: got keyboard signal\r\n")
|
56
|
+
if (SetConsoleCtrlHandler(@slow_console_handler, 0) = 0) then
|
57
|
+
fprintf(stderr, !"err: cannot unset console handler\r\n")
|
58
|
+
end if
|
59
|
+
end sub
|
60
|
+
|
61
|
+
function main() as integer
|
62
|
+
fprintf(stdout, !"out: message\r\n")
|
63
|
+
fprintf(stderr, !"err: error\r\n")
|
64
|
+
|
65
|
+
select case lcase(command(1))
|
66
|
+
case "wait":
|
67
|
+
sleep
|
68
|
+
return 0
|
69
|
+
|
70
|
+
case "error":
|
71
|
+
'# terminate with error code
|
72
|
+
return 1
|
73
|
+
|
74
|
+
case "slow1":
|
75
|
+
wait_for(1)
|
76
|
+
return 10
|
77
|
+
|
78
|
+
case "slow2":
|
79
|
+
wait_for(2)
|
80
|
+
return 20
|
81
|
+
end select
|
82
|
+
end function
|
83
|
+
|
84
|
+
control_signal = condcreate()
|
85
|
+
control_mutex = mutexcreate()
|
86
|
+
|
87
|
+
result = main()
|
88
|
+
|
89
|
+
conddestroy(control_signal)
|
90
|
+
mutexdestroy(control_mutex)
|
91
|
+
|
92
|
+
end result
|
@@ -0,0 +1,402 @@
|
|
1
|
+
'#--
|
2
|
+
'# Copyright (c) 2006-2007 Luis Lavena, Multimedia systems
|
3
|
+
'#
|
4
|
+
'# This source code is released under the MIT License.
|
5
|
+
'# See MIT-LICENSE file for details
|
6
|
+
'#++
|
7
|
+
|
8
|
+
#include once "console_process.bi"
|
9
|
+
#include once "file.bi"
|
10
|
+
#include once "testly.bi"
|
11
|
+
#include once "test_helpers.bi"
|
12
|
+
|
13
|
+
namespace Suite_Test_Console_Process
|
14
|
+
'# test helpers
|
15
|
+
declare function process_cleanup() as boolean
|
16
|
+
|
17
|
+
dim shared child as ConsoleProcess ptr
|
18
|
+
|
19
|
+
sub before_all()
|
20
|
+
kill("out.log")
|
21
|
+
kill("err.log")
|
22
|
+
kill("both.log")
|
23
|
+
kill("both_slow.log")
|
24
|
+
kill("both_forced.log")
|
25
|
+
end sub
|
26
|
+
|
27
|
+
sub after_each()
|
28
|
+
process_cleanup()
|
29
|
+
end sub
|
30
|
+
|
31
|
+
sub test_process_create()
|
32
|
+
child = new ConsoleProcess()
|
33
|
+
assert_not_equal(0, child)
|
34
|
+
assert_equal("", child->filename)
|
35
|
+
assert_equal("", child->arguments)
|
36
|
+
assert_false(child->running)
|
37
|
+
delete child
|
38
|
+
end sub
|
39
|
+
|
40
|
+
sub test_process_create_args()
|
41
|
+
child = new ConsoleProcess("mock_process.exe", "some params")
|
42
|
+
assert_equal("mock_process.exe", child->filename)
|
43
|
+
assert_equal("some params", child->arguments)
|
44
|
+
delete child
|
45
|
+
end sub
|
46
|
+
|
47
|
+
sub test_properly_quoted_filename()
|
48
|
+
child = new ConsoleProcess("C:\path with spaces\my_executable.exe", "some params")
|
49
|
+
assert_not_equal(0, instr(child->filename, !"\""))
|
50
|
+
delete child
|
51
|
+
end sub
|
52
|
+
|
53
|
+
sub test_failed_unexistant_process()
|
54
|
+
child = new ConsoleProcess("no_valid_file.exe", "some params")
|
55
|
+
assert_false(child->start())
|
56
|
+
assert_equal(0, child->pid)
|
57
|
+
assert_false(child->running)
|
58
|
+
delete child
|
59
|
+
end sub
|
60
|
+
|
61
|
+
sub test_process_spawn_exit_code()
|
62
|
+
child = new ConsoleProcess("mock_process.exe", "error")
|
63
|
+
|
64
|
+
'# start() should return true since it started, no matter if was terminated
|
65
|
+
'# improperly
|
66
|
+
assert_true(child->start())
|
67
|
+
sleep 500
|
68
|
+
|
69
|
+
'# should not be running, but pid should be != than 0
|
70
|
+
assert_not_equal(0, child->pid)
|
71
|
+
|
72
|
+
'# we need to wait a bit prior asking for state
|
73
|
+
'# the process could be still running
|
74
|
+
assert_false(child->running)
|
75
|
+
|
76
|
+
'# get exit code, should be 1
|
77
|
+
assert_equal(1, child->exit_code)
|
78
|
+
|
79
|
+
delete child
|
80
|
+
end sub
|
81
|
+
|
82
|
+
sub test_redirected_output()
|
83
|
+
assert_false(fileexists("out.log"))
|
84
|
+
assert_false(fileexists("err.log"))
|
85
|
+
|
86
|
+
'# redirected output is used with logging files.
|
87
|
+
child = new ConsoleProcess("mock_process.exe")
|
88
|
+
|
89
|
+
'# redirect stdout
|
90
|
+
assert_true(child->redirect(ProcessStdOut, "out.log"))
|
91
|
+
assert_string_equal("out.log", child->redirected_stdout)
|
92
|
+
|
93
|
+
'# redirect stderr
|
94
|
+
assert_true(child->redirect(ProcessStdErr, "err.log"))
|
95
|
+
assert_string_equal("err.log", child->redirected_stderr)
|
96
|
+
|
97
|
+
'# start() will be true since process terminated nicely
|
98
|
+
assert_true(child->start())
|
99
|
+
sleep 500
|
100
|
+
|
101
|
+
'# running should be false
|
102
|
+
assert_false(child->running)
|
103
|
+
|
104
|
+
'# exit_code should be 0
|
105
|
+
assert_equal(0, child->exit_code)
|
106
|
+
|
107
|
+
delete child
|
108
|
+
|
109
|
+
'# now out.log and err.log must exist and content must be valid.
|
110
|
+
assert_true(fileexists("out.log"))
|
111
|
+
assert_string_equal("out: message", content_of_file("out.log"))
|
112
|
+
|
113
|
+
assert_true(fileexists("err.log"))
|
114
|
+
assert_string_equal("err: error", content_of_file("err.log"))
|
115
|
+
|
116
|
+
assert_equal(0, kill("out.log"))
|
117
|
+
assert_equal(0, kill("err.log"))
|
118
|
+
end sub
|
119
|
+
|
120
|
+
sub test_redirected_merged_output()
|
121
|
+
dim content as string
|
122
|
+
|
123
|
+
'# redirected output is used with logging files.
|
124
|
+
child = new ConsoleProcess("mock_process.exe")
|
125
|
+
|
126
|
+
'# redirect both stdout and stderr
|
127
|
+
child->redirect(ProcessStdBoth, "both.log")
|
128
|
+
assert_equal("both.log", child->redirected_stdout)
|
129
|
+
assert_equal("both.log", child->redirected_stderr)
|
130
|
+
|
131
|
+
'# start() will be true since process terminated nicely
|
132
|
+
assert_true(child->start())
|
133
|
+
sleep 500
|
134
|
+
|
135
|
+
'# running should be false
|
136
|
+
assert_false(child->running)
|
137
|
+
|
138
|
+
'# exit_code should be 0
|
139
|
+
assert_equal(0, child->exit_code)
|
140
|
+
|
141
|
+
delete child
|
142
|
+
|
143
|
+
'# file must exists
|
144
|
+
assert_true(fileexists("both.log"))
|
145
|
+
|
146
|
+
'# contents must match
|
147
|
+
content = content_of_file("both.log")
|
148
|
+
|
149
|
+
assert_not_equal(0, instr(content, "out: message"))
|
150
|
+
assert_not_equal(0, instr(content, "err: error"))
|
151
|
+
|
152
|
+
assert_equal(0, kill("both.log"))
|
153
|
+
end sub
|
154
|
+
|
155
|
+
sub test_redirected_output_append()
|
156
|
+
dim content as string
|
157
|
+
|
158
|
+
child = new ConsoleProcess("mock_process.exe")
|
159
|
+
|
160
|
+
'# redirect both stdout and stderr
|
161
|
+
child->redirect(ProcessStdBoth, "both.log")
|
162
|
+
|
163
|
+
'# start() will be true since process terminated nicely
|
164
|
+
assert_true(child->start())
|
165
|
+
sleep 500
|
166
|
+
|
167
|
+
content = content_of_file("both.log")
|
168
|
+
|
169
|
+
'# start() again
|
170
|
+
assert_true(child->start())
|
171
|
+
sleep 500
|
172
|
+
|
173
|
+
delete child
|
174
|
+
|
175
|
+
assert_not_equal(len(content), len(content_of_file("both.log")))
|
176
|
+
|
177
|
+
assert_equal(0, kill("both.log"))
|
178
|
+
end sub
|
179
|
+
|
180
|
+
sub test_process_terminate()
|
181
|
+
dim content as string
|
182
|
+
|
183
|
+
'# redirected output is used with logging files.
|
184
|
+
child = new ConsoleProcess("mock_process.exe", "wait")
|
185
|
+
child->redirect(ProcessStdBoth, "both.log")
|
186
|
+
|
187
|
+
'# start
|
188
|
+
assert_true(child->start())
|
189
|
+
sleep 500
|
190
|
+
|
191
|
+
'# validate if running
|
192
|
+
assert_true(child->running)
|
193
|
+
|
194
|
+
'# validate PID
|
195
|
+
assert_not_equal(0, child->pid)
|
196
|
+
|
197
|
+
'# now terminates it
|
198
|
+
assert_true(child->terminate())
|
199
|
+
sleep 500
|
200
|
+
|
201
|
+
assert_equal(9, child->exit_code)
|
202
|
+
|
203
|
+
'# it should be done
|
204
|
+
assert_false(child->running)
|
205
|
+
|
206
|
+
delete child
|
207
|
+
|
208
|
+
'# validate output
|
209
|
+
'# file must exists
|
210
|
+
assert_true(fileexists("both.log"))
|
211
|
+
|
212
|
+
'# contents must match
|
213
|
+
content = content_of_file("both.log")
|
214
|
+
|
215
|
+
assert_not_equal(0, instr(content, "out: message"))
|
216
|
+
assert_not_equal(0, instr(content, "err: error"))
|
217
|
+
assert_not_equal(0, instr(content, "interrupted"))
|
218
|
+
|
219
|
+
assert_equal(0, kill("both.log"))
|
220
|
+
end sub
|
221
|
+
|
222
|
+
sub test_process_terminate_slow1()
|
223
|
+
dim content as string
|
224
|
+
|
225
|
+
'# redirected output is used with logging files.
|
226
|
+
child = new ConsoleProcess("mock_process.exe", "slow1")
|
227
|
+
child->redirect(ProcessStdBoth, "both_slow1.log")
|
228
|
+
|
229
|
+
'# start
|
230
|
+
assert_true(child->start())
|
231
|
+
sleep 500
|
232
|
+
|
233
|
+
'# validate if running
|
234
|
+
assert_true(child->running)
|
235
|
+
|
236
|
+
'# validate PID
|
237
|
+
assert_not_equal(0, child->pid)
|
238
|
+
|
239
|
+
'# now terminates it
|
240
|
+
assert_true(child->terminate())
|
241
|
+
sleep 500
|
242
|
+
|
243
|
+
assert_equal(10, child->exit_code)
|
244
|
+
|
245
|
+
'# it should be done
|
246
|
+
assert_false(child->running)
|
247
|
+
|
248
|
+
delete child
|
249
|
+
|
250
|
+
'# validate output
|
251
|
+
'# file must exists
|
252
|
+
assert_true(fileexists("both_slow1.log"))
|
253
|
+
|
254
|
+
'# contents must match
|
255
|
+
content = content_of_file("both_slow1.log")
|
256
|
+
|
257
|
+
assert_equal(0, instr(content, "interrupted"))
|
258
|
+
assert_not_equal(0, instr(content, "out: CTRL-C received"))
|
259
|
+
assert_equal(0, instr(content, "out: CTRL-BREAK received"))
|
260
|
+
|
261
|
+
assert_equal(0, kill("both_slow1.log"))
|
262
|
+
end sub
|
263
|
+
|
264
|
+
sub test_process_terminate_slow2()
|
265
|
+
dim content as string
|
266
|
+
|
267
|
+
'# redirected output is used with logging files.
|
268
|
+
child = new ConsoleProcess("mock_process.exe", "slow2")
|
269
|
+
child->redirect(ProcessStdBoth, "both_slow2.log")
|
270
|
+
|
271
|
+
'# start
|
272
|
+
assert_true(child->start())
|
273
|
+
sleep 500
|
274
|
+
|
275
|
+
'# validate if running
|
276
|
+
assert_true(child->running)
|
277
|
+
|
278
|
+
'# validate PID
|
279
|
+
assert_not_equal(0, child->pid)
|
280
|
+
|
281
|
+
'# now terminates it
|
282
|
+
assert_true(child->terminate())
|
283
|
+
sleep 500
|
284
|
+
|
285
|
+
assert_equal(20, child->exit_code)
|
286
|
+
|
287
|
+
'# it should be done
|
288
|
+
assert_false(child->running)
|
289
|
+
|
290
|
+
delete child
|
291
|
+
|
292
|
+
'# validate output
|
293
|
+
'# file must exists
|
294
|
+
assert_true(fileexists("both_slow2.log"))
|
295
|
+
|
296
|
+
'# contents must match
|
297
|
+
content = content_of_file("both_slow2.log")
|
298
|
+
|
299
|
+
assert_equal(0, instr(content, "interrupted"))
|
300
|
+
assert_not_equal(0, instr(content, "out: CTRL-C received"))
|
301
|
+
assert_not_equal(0, instr(content, "out: CTRL-BREAK received"))
|
302
|
+
|
303
|
+
'# cleanup
|
304
|
+
assert_equal(0, kill("both_slow2.log"))
|
305
|
+
end sub
|
306
|
+
|
307
|
+
sub test_process_terminate_forced()
|
308
|
+
dim content as string
|
309
|
+
|
310
|
+
'# redirected output is used with logging files.
|
311
|
+
child = new ConsoleProcess("mock_process.exe", "wait")
|
312
|
+
child->redirect(ProcessStdBoth, "both_forced.log")
|
313
|
+
|
314
|
+
'# start
|
315
|
+
assert_true(child->start())
|
316
|
+
sleep 500
|
317
|
+
|
318
|
+
'# validate if running
|
319
|
+
assert_true(child->running)
|
320
|
+
|
321
|
+
'# validate PID
|
322
|
+
assert_not_equal(0, child->pid)
|
323
|
+
|
324
|
+
'# now terminates it
|
325
|
+
assert_true(child->terminate(true))
|
326
|
+
sleep 500
|
327
|
+
|
328
|
+
'# it should be done
|
329
|
+
assert_false(child->running)
|
330
|
+
|
331
|
+
'# look for termination code
|
332
|
+
assert_equal(0, child->exit_code)
|
333
|
+
|
334
|
+
delete child
|
335
|
+
|
336
|
+
'# validate output
|
337
|
+
'# file must exists
|
338
|
+
assert_true(fileexists("both_forced.log"))
|
339
|
+
|
340
|
+
'# contents must match
|
341
|
+
content = content_of_file("both_forced.log")
|
342
|
+
|
343
|
+
assert_equal(0, instr(content, "out: message"))
|
344
|
+
assert_equal(0, instr(content, "err: error"))
|
345
|
+
assert_equal(0, instr(content, "interrupted"))
|
346
|
+
|
347
|
+
assert_equal(0, kill("both_forced.log"))
|
348
|
+
end sub
|
349
|
+
|
350
|
+
sub test_reuse_object_instance()
|
351
|
+
dim first_pid as uinteger
|
352
|
+
|
353
|
+
child = new ConsoleProcess("mock_process.exe")
|
354
|
+
|
355
|
+
'# start
|
356
|
+
assert_true(child->start())
|
357
|
+
sleep 500
|
358
|
+
|
359
|
+
'# validate not running
|
360
|
+
assert_false(child->running)
|
361
|
+
|
362
|
+
'# validate PID
|
363
|
+
assert_not_equal(0, child->pid)
|
364
|
+
|
365
|
+
'# saves PID
|
366
|
+
first_pid = child->pid
|
367
|
+
|
368
|
+
'# start it again
|
369
|
+
assert_true(child->start())
|
370
|
+
sleep 500
|
371
|
+
|
372
|
+
'# it should have stopped by now
|
373
|
+
assert_false(child->running)
|
374
|
+
assert_not_equal(0, child->pid)
|
375
|
+
assert_not_equal(first_pid, child->pid)
|
376
|
+
|
377
|
+
delete child
|
378
|
+
end sub
|
379
|
+
|
380
|
+
private sub register() constructor
|
381
|
+
add_suite(Suite_Test_Console_Process)
|
382
|
+
add_test(test_process_create)
|
383
|
+
add_test(test_process_create_args)
|
384
|
+
add_test(test_properly_quoted_filename)
|
385
|
+
add_test(test_failed_unexistant_process)
|
386
|
+
add_test(test_process_spawn_exit_code)
|
387
|
+
add_test(test_redirected_output)
|
388
|
+
add_test(test_redirected_merged_output)
|
389
|
+
add_test(test_redirected_output_append)
|
390
|
+
add_test(test_process_terminate)
|
391
|
+
add_test(test_process_terminate_slow1)
|
392
|
+
add_test(test_process_terminate_slow2)
|
393
|
+
add_test(test_process_terminate_forced)
|
394
|
+
add_test(test_reuse_object_instance)
|
395
|
+
end sub
|
396
|
+
|
397
|
+
'# test helpers below this point
|
398
|
+
private function process_cleanup() as boolean
|
399
|
+
shell "taskkill /f /im mock_process.exe 1>NUL 2>&1"
|
400
|
+
return true
|
401
|
+
end function
|
402
|
+
end namespace
|