rutema 2.0.0 → 2.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.
- checksums.yaml +5 -5
- data/History.txt +2 -0
- data/README.md +3 -3
- data/bin/rutema +4 -2
- data/lib/rutema/application.rb +22 -4
- data/lib/rutema/core/configuration.rb +277 -98
- data/lib/rutema/core/engine.rb +177 -33
- data/lib/rutema/core/framework.rb +115 -26
- data/lib/rutema/core/objectmodel.rb +284 -222
- data/lib/rutema/core/parser.rb +41 -10
- data/lib/rutema/core/reporter.rb +39 -16
- data/lib/rutema/core/runner.rb +103 -35
- data/lib/rutema/elements/minimal.rb +12 -9
- data/lib/rutema/parsers/xml.rb +63 -11
- data/lib/rutema/reporters/json.rb +4 -2
- data/lib/rutema/reporters/junit.rb +35 -12
- data/lib/rutema/version.rb +21 -6
- metadata +24 -20
data/lib/rutema/core/engine.rb
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
# Copyright (c) 2007-
|
|
1
|
+
# Copyright (c) 2007-2021 Vassilis Rizopoulos. All rights reserved.
|
|
2
|
+
|
|
2
3
|
require 'thread'
|
|
3
4
|
require_relative 'parser'
|
|
4
5
|
require_relative 'reporter'
|
|
@@ -6,13 +7,28 @@ require_relative 'runner'
|
|
|
6
7
|
require_relative '../version'
|
|
7
8
|
|
|
8
9
|
module Rutema
|
|
9
|
-
|
|
10
|
+
##
|
|
11
|
+
# Class implementing the inner workflow of rutema
|
|
10
12
|
#
|
|
11
|
-
#
|
|
13
|
+
# This class takes care of instantiating the configured parser, runner and
|
|
14
|
+
# reporters. The reporters then receive event information from the runner
|
|
15
|
+
# through a Dispatcher instance.
|
|
12
16
|
#
|
|
13
|
-
#The full workflow
|
|
17
|
+
# The full workflow consists of subsequent +parse+, +run+ and +report+ phases
|
|
18
|
+
# and corresponds to one call of the #run method.
|
|
14
19
|
class Engine
|
|
15
20
|
include Messaging
|
|
21
|
+
|
|
22
|
+
##
|
|
23
|
+
# Initialize a new Engine instance and setup all class instances needed for
|
|
24
|
+
# test execution
|
|
25
|
+
#
|
|
26
|
+
# This brings up a parser, the runner and the reporters and wires them up as
|
|
27
|
+
# needed. After completion of this method the instance is ready for test
|
|
28
|
+
# execution by means of the #run method.
|
|
29
|
+
#
|
|
30
|
+
# * +configuration+ - a Configuration instance according to which Engine,
|
|
31
|
+
# its components and the test run shall be set up
|
|
16
32
|
def initialize configuration
|
|
17
33
|
@queue=Queue.new
|
|
18
34
|
@parser=instantiate_class(configuration.parser,configuration) if configuration.parser
|
|
@@ -29,27 +45,38 @@ module Rutema
|
|
|
29
45
|
@dispatcher=Dispatcher.new(@queue,configuration)
|
|
30
46
|
@configuration=configuration
|
|
31
47
|
end
|
|
32
|
-
|
|
48
|
+
|
|
49
|
+
##
|
|
50
|
+
# Parse the test specifications, execute the test(s) and report the results
|
|
51
|
+
#
|
|
52
|
+
# * +test_identifier+ - an optional identifier of a single test case to be
|
|
53
|
+
# executed, this cannot be an arbitrary one but must still be contained
|
|
54
|
+
# in the configured test specification identifiers
|
|
33
55
|
def run test_identifier=nil
|
|
34
56
|
@dispatcher.run!
|
|
35
|
-
#start
|
|
57
|
+
# start
|
|
36
58
|
message("start")
|
|
37
59
|
suite_setup,suite_teardown,setup,teardown,tests=*parse(test_identifier)
|
|
38
60
|
if tests.empty?
|
|
39
61
|
@dispatcher.exit
|
|
40
62
|
raise RutemaError,"No tests to run!"
|
|
41
63
|
else
|
|
42
|
-
|
|
43
|
-
@runner.teardown=teardown
|
|
44
|
-
#running - at this point we've done any and all checks and we're stepping on the gas
|
|
64
|
+
# running - at this point all checks are done and the tests are active
|
|
45
65
|
message("running")
|
|
46
|
-
run_scenarios(tests,suite_setup,suite_teardown)
|
|
66
|
+
run_scenarios(tests,suite_setup,suite_teardown,setup,teardown)
|
|
47
67
|
end
|
|
48
68
|
message("end")
|
|
49
69
|
@dispatcher.exit
|
|
50
70
|
@dispatcher.report(tests)
|
|
51
71
|
end
|
|
52
|
-
|
|
72
|
+
|
|
73
|
+
##
|
|
74
|
+
# Parse a single test specification or all the specs given by the
|
|
75
|
+
# configuration
|
|
76
|
+
#
|
|
77
|
+
# * +test_identifier+ - an optional identifier of a single test case to be
|
|
78
|
+
# executed, this cannot be an arbitrary one but must still be contained
|
|
79
|
+
# in the configured test specification identifiers
|
|
53
80
|
def parse test_identifier=nil
|
|
54
81
|
specs=[]
|
|
55
82
|
#so, while we are parsing, we have a list of tests
|
|
@@ -68,12 +95,27 @@ module Rutema
|
|
|
68
95
|
suite_setup,suite_teardown,setup,teardown=parse_specials(@configuration)
|
|
69
96
|
return [suite_setup,suite_teardown,setup,teardown,specs]
|
|
70
97
|
end
|
|
98
|
+
|
|
71
99
|
private
|
|
100
|
+
|
|
101
|
+
##
|
|
102
|
+
# Parse an array of test specifications into Specification instances
|
|
103
|
+
#
|
|
104
|
+
# * +tests+ - an array containing paths to test specification files or the
|
|
105
|
+
# test specifications' texts themselves
|
|
72
106
|
def parse_specifications tests
|
|
73
107
|
tests.map do |t|
|
|
74
108
|
parse_specification(t)
|
|
75
109
|
end.compact
|
|
76
110
|
end
|
|
111
|
+
|
|
112
|
+
##
|
|
113
|
+
# Parse a single test specification into a Specification instance
|
|
114
|
+
#
|
|
115
|
+
# * +spec_identifier+ - either the path to a test specification file or the
|
|
116
|
+
# actual test specification text itself
|
|
117
|
+
#
|
|
118
|
+
# A ParserError is raised upon failure.
|
|
77
119
|
def parse_specification spec_identifier
|
|
78
120
|
begin
|
|
79
121
|
@parser.parse_specification(spec_identifier)
|
|
@@ -82,6 +124,15 @@ module Rutema
|
|
|
82
124
|
raise Rutema::ParserError, "In #{spec_identifier}: #{$!.message}"
|
|
83
125
|
end
|
|
84
126
|
end
|
|
127
|
+
|
|
128
|
+
##
|
|
129
|
+
# Parse the special test (suite) setup and teardown methods if set
|
|
130
|
+
#
|
|
131
|
+
# This returns an array containing Specification instances for
|
|
132
|
+
# * test suite setup
|
|
133
|
+
# * test suite teardown
|
|
134
|
+
# * test setup
|
|
135
|
+
# * test teardown
|
|
85
136
|
def parse_specials configuration
|
|
86
137
|
suite_setup=nil
|
|
87
138
|
suite_teardown=nil
|
|
@@ -101,33 +152,55 @@ module Rutema
|
|
|
101
152
|
end
|
|
102
153
|
return suite_setup,suite_teardown,setup,teardown
|
|
103
154
|
end
|
|
104
|
-
|
|
155
|
+
|
|
156
|
+
##
|
|
157
|
+
# Execute the passed test Specification instances
|
|
158
|
+
#
|
|
159
|
+
# * +specs+ - an array of Specification instances representing the actual
|
|
160
|
+
# tests
|
|
161
|
+
# * +suite_setup+ - a test suite setup Specification instance
|
|
162
|
+
# * +suite_teardown+ - a test suite teardown Specification instance
|
|
163
|
+
def run_scenarios(specs, suite_setup, suite_teardown, setup, teardown)
|
|
105
164
|
if specs.empty?
|
|
106
165
|
error(nil,"No tests to run")
|
|
107
166
|
else
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
else
|
|
167
|
+
@runner.setup = nil
|
|
168
|
+
@runner.teardown = nil
|
|
169
|
+
|
|
170
|
+
if !suite_setup || (run_test(suite_setup, true) == :success)
|
|
171
|
+
@runner.setup = setup
|
|
172
|
+
@runner.teardown = teardown
|
|
115
173
|
specs.each{|spec| run_test(spec)}
|
|
174
|
+
else
|
|
175
|
+
error(nil, "Suite setup test failed")
|
|
116
176
|
end
|
|
117
177
|
if suite_teardown
|
|
118
|
-
|
|
178
|
+
@runner.setup = nil
|
|
179
|
+
@runner.teardown = nil
|
|
180
|
+
run_test(suite_teardown, true)
|
|
119
181
|
end
|
|
120
182
|
end
|
|
121
183
|
end
|
|
122
|
-
|
|
184
|
+
|
|
185
|
+
##
|
|
186
|
+
#
|
|
187
|
+
def run_test(specification, is_special = false)
|
|
123
188
|
if specification.scenario
|
|
124
|
-
status
|
|
189
|
+
status = @runner.run(specification, is_special)["status"]
|
|
125
190
|
else
|
|
126
191
|
status=:not_executed
|
|
127
192
|
message(:test=>specification.name,:text=>"No scenario", :status=>status)
|
|
128
193
|
end
|
|
129
194
|
return status
|
|
130
195
|
end
|
|
196
|
+
|
|
197
|
+
##
|
|
198
|
+
# Instantiate a new class of a given type passing it a given configuration
|
|
199
|
+
# upon construction
|
|
200
|
+
#
|
|
201
|
+
# * +definition+ - class of which a new instance shall be instantiated
|
|
202
|
+
# * +configuration+ - Configuration instance which shall be passed to the
|
|
203
|
+
# initializer of the to be created instance
|
|
131
204
|
def instantiate_class definition,configuration
|
|
132
205
|
if definition[:class]
|
|
133
206
|
klass=definition[:class]
|
|
@@ -135,26 +208,55 @@ module Rutema
|
|
|
135
208
|
end
|
|
136
209
|
return nil
|
|
137
210
|
end
|
|
211
|
+
|
|
212
|
+
##
|
|
213
|
+
# Check if the given test identifier belongs to the normal test cases or to
|
|
214
|
+
# one of the special ones (the test (suite) setups and teardowns)
|
|
215
|
+
#
|
|
216
|
+
# * +test_identifier+ - the test identifier to check against membership in
|
|
217
|
+
# the normal or special test case identifier sets
|
|
138
218
|
def is_spec_included? test_identifier
|
|
139
219
|
full_path=File.expand_path(test_identifier)
|
|
140
|
-
return @configuration.tests.include?(full_path) || is_special?(test_identifier)
|
|
220
|
+
return @configuration.tests.include?(full_path) || is_special?(test_identifier)
|
|
141
221
|
end
|
|
222
|
+
|
|
223
|
+
##
|
|
224
|
+
# Check if the given test identifier is a (suite) setup or teardown one
|
|
225
|
+
#
|
|
226
|
+
# This checks if the given identifier was configured as (suite) setup or
|
|
227
|
+
# teardown within the rutema run's configuration.
|
|
228
|
+
#
|
|
229
|
+
# * +test_identifier+ - the test identifier which shall be checked for being
|
|
230
|
+
# a special one
|
|
142
231
|
def is_special? test_identifier
|
|
143
232
|
full_path=File.expand_path(test_identifier)
|
|
144
233
|
return full_path==@configuration.suite_setup ||
|
|
145
234
|
full_path==@configuration.suite_teardown ||
|
|
146
235
|
full_path==@configuration.setup ||
|
|
147
|
-
full_path==@configuration.teardown
|
|
236
|
+
full_path==@configuration.teardown
|
|
148
237
|
end
|
|
149
238
|
end
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
#
|
|
239
|
+
|
|
240
|
+
##
|
|
241
|
+
# Class functioning as a demultiplexer between the Engine and the various
|
|
242
|
+
# Reporters instances
|
|
153
243
|
#
|
|
154
|
-
#
|
|
244
|
+
# In stream mode the incoming queue is popped periodically and the messages
|
|
245
|
+
# are distributed to the queues of any subscribed event reporters. By default
|
|
246
|
+
# this includes Reporters::Collector which is then used at the end of a run to
|
|
247
|
+
# provide the collected data to all registered block mode reporters
|
|
155
248
|
class Dispatcher
|
|
156
|
-
|
|
157
|
-
|
|
249
|
+
##
|
|
250
|
+
# The interval between queue operations
|
|
251
|
+
INTERVAL = 0.01
|
|
252
|
+
|
|
253
|
+
##
|
|
254
|
+
# Initialize a new demultiplexer and instantiate all reporters requested by
|
|
255
|
+
# the passed configuration
|
|
256
|
+
#
|
|
257
|
+
# * +queue+ - the queue which will be shared between the Engine instance and
|
|
258
|
+
# the Reporter instances
|
|
259
|
+
# * +configuration+ - the Configuration instance of the rutema run
|
|
158
260
|
def initialize queue,configuration
|
|
159
261
|
@queue = queue
|
|
160
262
|
@queues = {}
|
|
@@ -169,12 +271,25 @@ module Rutema
|
|
|
169
271
|
@streaming_reporters<<@collector
|
|
170
272
|
@configuration=configuration
|
|
171
273
|
end
|
|
172
|
-
|
|
274
|
+
|
|
275
|
+
##
|
|
276
|
+
# Call this to establish a queue with the given identifier
|
|
277
|
+
#
|
|
278
|
+
# This method will create a new queue within the Dispatcher instance into
|
|
279
|
+
# which data from the incoming queue from the Engine instance will be
|
|
280
|
+
# dispatched to.
|
|
281
|
+
#
|
|
282
|
+
# * +identifier+ - a unique identifier for the queue. If two identifiers
|
|
283
|
+
# collide the new subscriber will replace the earlier one
|
|
173
284
|
def subscribe identifier
|
|
174
285
|
@queues[identifier]=Queue.new
|
|
175
286
|
return @queues[identifier]
|
|
176
287
|
end
|
|
177
|
-
|
|
288
|
+
|
|
289
|
+
##
|
|
290
|
+
# Start #update threads of all event/streaming reporters and then start a
|
|
291
|
+
# new locally managed thread which continually dispatches messages from the
|
|
292
|
+
# incoming queue
|
|
178
293
|
def run!
|
|
179
294
|
puts "Running #{@streaming_reporters.size} streaming reporters" if $DEBUG
|
|
180
295
|
@streaming_reporters.each {|r| r.run!}
|
|
@@ -186,12 +301,19 @@ module Rutema
|
|
|
186
301
|
end
|
|
187
302
|
end
|
|
188
303
|
|
|
304
|
+
##
|
|
305
|
+
# Call all block reporters' BlockReporter#report method
|
|
189
306
|
def report specs
|
|
190
307
|
@block_reporters.each do |r|
|
|
191
308
|
r.report(specs,@collector.states,@collector.errors)
|
|
192
309
|
end
|
|
193
310
|
Reporters::Summary.new(@configuration,self).report(specs,@collector.states,@collector.errors)
|
|
194
311
|
end
|
|
312
|
+
|
|
313
|
+
##
|
|
314
|
+
# Dispatch all messages in the incoming queue to the subscribed reporters,
|
|
315
|
+
# exit all streaming reporters' threads and then stop the own internal
|
|
316
|
+
# dispatch thread
|
|
195
317
|
def exit
|
|
196
318
|
puts "Exiting main dispatcher" if $DEBUG
|
|
197
319
|
if @thread
|
|
@@ -200,7 +322,12 @@ module Rutema
|
|
|
200
322
|
Thread.kill(@thread)
|
|
201
323
|
end
|
|
202
324
|
end
|
|
325
|
+
|
|
203
326
|
private
|
|
327
|
+
|
|
328
|
+
##
|
|
329
|
+
# Dispatch messages from the incoming queue to all subscribers until the
|
|
330
|
+
# incoming queue is empty
|
|
204
331
|
def flush
|
|
205
332
|
puts "Flushing queues" if $DEBUG
|
|
206
333
|
if @thread
|
|
@@ -210,6 +337,18 @@ module Rutema
|
|
|
210
337
|
end
|
|
211
338
|
end
|
|
212
339
|
end
|
|
340
|
+
|
|
341
|
+
##
|
|
342
|
+
# Instantiate a new reporter instance and pass the configuration and this
|
|
343
|
+
# Dispatcher instance this method is called upon to
|
|
344
|
+
#
|
|
345
|
+
# * +definition+ - hash containing the class type which shall be
|
|
346
|
+
# instantiated referenced by its +:class+ key
|
|
347
|
+
# * +configuration+ - the Configuration instance which shall be passed to
|
|
348
|
+
# the initializer of the to be instantiated class
|
|
349
|
+
#
|
|
350
|
+
# This either returns the new class instance or _nil_ if the passed hash
|
|
351
|
+
# did not contain a key +:class+
|
|
213
352
|
def instantiate_reporter definition,configuration
|
|
214
353
|
if definition[:class]
|
|
215
354
|
klass=definition[:class]
|
|
@@ -217,6 +356,11 @@ module Rutema
|
|
|
217
356
|
end
|
|
218
357
|
return nil
|
|
219
358
|
end
|
|
359
|
+
|
|
360
|
+
##
|
|
361
|
+
# Pop the last element of the incoming queue from the runner (if it's not
|
|
362
|
+
# empty) and distribute it to all subscribed Reporters::EventReporter
|
|
363
|
+
# instances
|
|
220
364
|
def dispatch
|
|
221
365
|
if @queue.size>0
|
|
222
366
|
data=@queue.pop
|
|
@@ -224,4 +368,4 @@ module Rutema
|
|
|
224
368
|
end
|
|
225
369
|
end
|
|
226
370
|
end
|
|
227
|
-
end
|
|
371
|
+
end
|
|
@@ -1,13 +1,37 @@
|
|
|
1
|
+
# Copyright (c) 2021 Vassilis Rizopoulos. All rights reserved.
|
|
2
|
+
|
|
1
3
|
module Rutema
|
|
2
|
-
|
|
4
|
+
STATUS_CODES=[:started,:skipped,:success,:warning,:error]
|
|
5
|
+
|
|
6
|
+
##
|
|
7
|
+
# Simple base for classes concerned with message passing to report test
|
|
8
|
+
# progress and failures
|
|
9
|
+
#
|
|
10
|
+
# This class and its descendants can be utilized as a container for data
|
|
11
|
+
# relevant to tests and their results. Currently they are being emitted by the
|
|
12
|
+
# Engine and Runners instances and consumed by classes within the Reporters
|
|
13
|
+
# module.
|
|
3
14
|
#
|
|
4
|
-
#
|
|
15
|
+
# Specialized descendants are ErrorMessage and RunnerMessage.
|
|
5
16
|
class Message
|
|
6
|
-
|
|
7
|
-
#
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
#
|
|
17
|
+
##
|
|
18
|
+
# The test whose execution originated the message
|
|
19
|
+
attr_accessor :test
|
|
20
|
+
##
|
|
21
|
+
# The text of the message
|
|
22
|
+
attr_accessor :text
|
|
23
|
+
##
|
|
24
|
+
# The timestamp of the message's creation
|
|
25
|
+
attr_accessor :timestamp
|
|
26
|
+
|
|
27
|
+
##
|
|
28
|
+
# Initialize a new message from data passed in a hash
|
|
29
|
+
#
|
|
30
|
+
# The following keys of the hash are being utilized:
|
|
31
|
+
# * +:test+ - the test id/name of the test which originates the message
|
|
32
|
+
# * +:text+ - the text of the message
|
|
33
|
+
# * +:timestamp+ - most often the timestamp of the creation of the message,
|
|
34
|
+
# defaults to +Time.now+
|
|
11
35
|
def initialize params
|
|
12
36
|
@test=params.fetch(:test,"")
|
|
13
37
|
@test||=""
|
|
@@ -15,6 +39,8 @@ module Rutema
|
|
|
15
39
|
@timestamp=params.fetch(:timestamp,Time.now)
|
|
16
40
|
end
|
|
17
41
|
|
|
42
|
+
##
|
|
43
|
+
# Convert the instance to a convenient textual representation
|
|
18
44
|
def to_s
|
|
19
45
|
msg=""
|
|
20
46
|
msg<<"#{@test} " unless @test.empty?
|
|
@@ -22,8 +48,16 @@ module Rutema
|
|
|
22
48
|
return msg
|
|
23
49
|
end
|
|
24
50
|
end
|
|
25
|
-
|
|
51
|
+
|
|
52
|
+
##
|
|
53
|
+
# Message container to report test errors
|
|
54
|
+
#
|
|
55
|
+
# The reported on errors may concern the test specifications, parser errors or
|
|
56
|
+
# errors which occurred during test execution. Logic errors of rutema itself
|
|
57
|
+
# are not reported by means of this class.
|
|
26
58
|
class ErrorMessage<Message
|
|
59
|
+
##
|
|
60
|
+
# Convert the instance to a convenient textual representation
|
|
27
61
|
def to_s
|
|
28
62
|
msg="ERROR - "
|
|
29
63
|
msg<<"#{@test} " unless @test.empty?
|
|
@@ -31,12 +65,26 @@ module Rutema
|
|
|
31
65
|
return msg
|
|
32
66
|
end
|
|
33
67
|
end
|
|
34
|
-
|
|
68
|
+
|
|
69
|
+
##
|
|
70
|
+
# Message container continually being emitted by runners (see Runners module)
|
|
71
|
+
# during test execution
|
|
35
72
|
#
|
|
36
|
-
#
|
|
37
|
-
#
|
|
73
|
+
# These messages inform about the progress of test execution. Test errors are
|
|
74
|
+
# propagated through instances of this class as well. If it's an engine error
|
|
75
|
+
# (e.g. during parsing), then an ErrorMessage will be used in that case.
|
|
38
76
|
class RunnerMessage<Message
|
|
39
|
-
attr_accessor :duration
|
|
77
|
+
attr_accessor :duration, :status, :number, :out, :err, :is_special
|
|
78
|
+
|
|
79
|
+
##
|
|
80
|
+
# Initialize a new runner message from data passed in a hash
|
|
81
|
+
#
|
|
82
|
+
# Additionally to the keys of the Message initializer the following keys of
|
|
83
|
+
# the hash are being utilized:
|
|
84
|
+
# * "duration" - the time a test step took for execution
|
|
85
|
+
# * "status" - the status of the respective step
|
|
86
|
+
# * +:timestamp+ - most often the timestamp of the creation of the message,
|
|
87
|
+
# defaults to +Time.now+
|
|
40
88
|
def initialize params
|
|
41
89
|
super(params)
|
|
42
90
|
@duration=params.fetch("duration",0)
|
|
@@ -44,13 +92,18 @@ module Rutema
|
|
|
44
92
|
@number=params.fetch("number",1)
|
|
45
93
|
@out=params.fetch("out","")
|
|
46
94
|
@err=params.fetch("err","")
|
|
95
|
+
@backtrace=params.fetch("backtrace","")
|
|
96
|
+
@is_special=params.fetch("is_special","")
|
|
47
97
|
end
|
|
48
98
|
|
|
99
|
+
##
|
|
100
|
+
# Convert the instance to a convenient textual representation
|
|
49
101
|
def to_s
|
|
50
102
|
msg="#{@test}:"
|
|
103
|
+
msg<<" #{@timestamp.strftime("%H:%M:%S")} :"
|
|
51
104
|
msg<<"#{@text}." unless @text.empty?
|
|
52
105
|
outpt=output()
|
|
53
|
-
msg<<" Output:\n#{outpt}" unless outpt.empty? || @status!=:error
|
|
106
|
+
msg<<" Output" + (outpt.empty? ? "." : ":\n#{outpt}") # unless outpt.empty? || @status!=:error
|
|
54
107
|
return msg
|
|
55
108
|
end
|
|
56
109
|
|
|
@@ -58,9 +111,11 @@ module Rutema
|
|
|
58
111
|
msg=""
|
|
59
112
|
msg<<"#{@out}\n" unless @out.empty?
|
|
60
113
|
msg<<@err unless @err.empty?
|
|
114
|
+
msg<<"\n" + (@backtrace.kind_of?(Array) ? @backtrace.join("\n") : @backtrace) unless @backtrace.empty?
|
|
61
115
|
return msg.chomp
|
|
62
116
|
end
|
|
63
117
|
end
|
|
118
|
+
|
|
64
119
|
#While executing tests the state of each test is collected in an
|
|
65
120
|
#instance of ReportState and the collection is at the end passed to the available block reporters
|
|
66
121
|
#
|
|
@@ -68,7 +123,7 @@ module Rutema
|
|
|
68
123
|
#and accumulates the duration reported by all messages in it's collection.
|
|
69
124
|
class ReportState
|
|
70
125
|
attr_accessor :steps
|
|
71
|
-
attr_reader :test
|
|
126
|
+
attr_reader :test, :timestamp, :duration, :status, :is_special
|
|
72
127
|
|
|
73
128
|
def initialize message
|
|
74
129
|
@test=message.test
|
|
@@ -76,25 +131,43 @@ module Rutema
|
|
|
76
131
|
@duration=message.duration
|
|
77
132
|
@status=message.status
|
|
78
133
|
@steps=[message]
|
|
134
|
+
@is_special=message.is_special
|
|
79
135
|
end
|
|
80
136
|
|
|
81
137
|
def <<(message)
|
|
82
138
|
@steps<<message
|
|
83
139
|
@duration+=message.duration
|
|
84
|
-
@status=message.status
|
|
140
|
+
@status = message.status unless message.status.nil? \
|
|
141
|
+
|| (!@status.nil? && STATUS_CODES.find_index(message.status) < STATUS_CODES.find_index(@status))
|
|
85
142
|
end
|
|
86
143
|
end
|
|
87
144
|
|
|
145
|
+
##
|
|
146
|
+
# Mix-in module which offers an interface to push messages to a queue
|
|
147
|
+
#
|
|
148
|
+
# Instances of the class including this module need a @queue member variable.
|
|
88
149
|
module Messaging
|
|
89
|
-
|
|
150
|
+
##
|
|
151
|
+
# Push a new ErrorMessage instance to the queue
|
|
152
|
+
#
|
|
153
|
+
# * +identifier+ - in most cases this would be the name of a test or its
|
|
154
|
+
# specification file
|
|
155
|
+
# * +message+ - a short descriptive message detailing the error condition
|
|
90
156
|
def error identifier,message
|
|
91
157
|
@queue.push(ErrorMessage.new(:test=>identifier,:text=>message,:timestamp=>Time.now))
|
|
92
158
|
end
|
|
93
|
-
|
|
159
|
+
|
|
160
|
+
##
|
|
161
|
+
# Push a new Message or RunnerMessage instance to the queue
|
|
162
|
+
#
|
|
163
|
+
# If +message+ is of type String a Message instance will be pushed to the
|
|
164
|
+
# queue. If it's of type Hash it will be passed to the initializer of
|
|
165
|
+
# RunnerMessage if it has both the keys :test and "status" or to the
|
|
166
|
+
# initializer of Message if not so.
|
|
94
167
|
def message message
|
|
95
168
|
case message
|
|
96
169
|
when String
|
|
97
|
-
Message.new(:text=>message,:timestamp=>Time.now)
|
|
170
|
+
@queue.push(Message.new(:text=>message,:timestamp=>Time.now))
|
|
98
171
|
when Hash
|
|
99
172
|
hm=Message.new(message)
|
|
100
173
|
hm=RunnerMessage.new(message) if message[:test] && message["status"]
|
|
@@ -103,16 +176,32 @@ module Rutema
|
|
|
103
176
|
end
|
|
104
177
|
end
|
|
105
178
|
end
|
|
106
|
-
|
|
179
|
+
|
|
180
|
+
##
|
|
181
|
+
# Generic error class which is being used as base class for all other rutema
|
|
182
|
+
# errors and for Engine related errors.
|
|
183
|
+
#
|
|
184
|
+
# This is being inherited by:
|
|
185
|
+
# * ParserError
|
|
186
|
+
# * ReportError
|
|
187
|
+
# * RunnerError
|
|
107
188
|
class RutemaError<RuntimeError
|
|
108
189
|
end
|
|
109
|
-
|
|
110
|
-
|
|
190
|
+
|
|
191
|
+
##
|
|
192
|
+
# Specialized error class particular to the parsing of rutema test
|
|
193
|
+
# specifications
|
|
194
|
+
class ParserError < RutemaError
|
|
111
195
|
end
|
|
112
|
-
|
|
113
|
-
|
|
196
|
+
|
|
197
|
+
##
|
|
198
|
+
# Specialized error class designated to errors within runner classes
|
|
199
|
+
class RunnerError < RutemaError
|
|
114
200
|
end
|
|
115
|
-
|
|
116
|
-
|
|
201
|
+
|
|
202
|
+
##
|
|
203
|
+
# Specialized error class which should be utilized by Reporters members to
|
|
204
|
+
# signal errors upon reporting
|
|
205
|
+
class ReportError < RutemaError
|
|
117
206
|
end
|
|
118
|
-
end
|
|
207
|
+
end
|