rutema 2.0.0.pre5 → 2.0.0.pre6
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 +4 -4
- data/History.txt +220 -213
- data/Manifest.txt +17 -18
- data/README.md +55 -55
- data/bin/rutema +7 -7
- data/lib/rutema/application.rb +61 -60
- data/lib/rutema/core/configuration.rb +207 -194
- data/lib/rutema/core/engine.rb +192 -185
- data/lib/rutema/core/framework.rb +88 -27
- data/lib/rutema/core/objectmodel.rb +0 -0
- data/lib/rutema/core/parser.rb +34 -34
- data/lib/rutema/core/reporter.rb +133 -129
- data/lib/rutema/core/runner.rb +93 -83
- data/lib/rutema/elements/minimal.rb +47 -47
- data/lib/rutema/parsers/xml.rb +156 -154
- data/lib/rutema/reporters/json.rb +34 -34
- data/lib/rutema/reporters/junit.rb +101 -97
- data/lib/rutema/version.rb +8 -8
- metadata +5 -7
- data/.gemtest +0 -0
- data/lib/rutema/reporters/nunit.rb +0 -103
data/lib/rutema/core/engine.rb
CHANGED
@@ -1,186 +1,193 @@
|
|
1
|
-
# Copyright (c) 2007-2015 Vassilis Rizopoulos. All rights reserved.
|
2
|
-
require 'thread'
|
3
|
-
require_relative 'parser'
|
4
|
-
require_relative 'reporter'
|
5
|
-
require_relative 'runner'
|
6
|
-
require_relative '../version'
|
7
|
-
|
8
|
-
module Rutema
|
9
|
-
class Engine
|
10
|
-
include Messaging
|
11
|
-
def initialize configuration
|
12
|
-
@queue=Queue.new
|
13
|
-
@parser=instantiate_class(configuration.parser,configuration) if configuration.parser
|
14
|
-
if configuration.runner
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
@dispatcher.
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
@
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
end
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
@
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
@
|
137
|
-
|
138
|
-
end
|
139
|
-
|
140
|
-
|
141
|
-
@
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
end
|
179
|
-
def
|
180
|
-
if
|
181
|
-
|
182
|
-
|
183
|
-
end
|
184
|
-
|
185
|
-
|
1
|
+
# Copyright (c) 2007-2015 Vassilis Rizopoulos. All rights reserved.
|
2
|
+
require 'thread'
|
3
|
+
require_relative 'parser'
|
4
|
+
require_relative 'reporter'
|
5
|
+
require_relative 'runner'
|
6
|
+
require_relative '../version'
|
7
|
+
|
8
|
+
module Rutema
|
9
|
+
class Engine
|
10
|
+
include Messaging
|
11
|
+
def initialize configuration
|
12
|
+
@queue=Queue.new
|
13
|
+
@parser=instantiate_class(configuration.parser,configuration) if configuration.parser
|
14
|
+
if configuration.runner
|
15
|
+
if configuration.runner[:class]
|
16
|
+
@runner=configuration.runner[:class].new(configuration.context,@queue)
|
17
|
+
else
|
18
|
+
raise RutemaError,"Runner settting overriden, but missing :class"
|
19
|
+
end
|
20
|
+
else
|
21
|
+
@runner=Rutema::Runners::Default.new(configuration.context,@queue)
|
22
|
+
end
|
23
|
+
raise RutemaError,"Could not instantiate parser" unless @parser
|
24
|
+
@dispatcher=Dispatcher.new(@queue,configuration)
|
25
|
+
@configuration=configuration
|
26
|
+
end
|
27
|
+
def run test_identifier=nil
|
28
|
+
@dispatcher.run!
|
29
|
+
#start
|
30
|
+
message("start")
|
31
|
+
check,setup,teardown,tests=*parse(test_identifier)
|
32
|
+
if tests.empty?
|
33
|
+
@dispatcher.exit
|
34
|
+
raise RutemaError,"Did not parse any tests succesfully"
|
35
|
+
else
|
36
|
+
@runner.setup=setup
|
37
|
+
@runner.teardown=teardown
|
38
|
+
#running - at this point we've done any and all checks and we're stepping on the gas
|
39
|
+
message("running")
|
40
|
+
run_scenarios(tests,check)
|
41
|
+
end
|
42
|
+
message("end")
|
43
|
+
@dispatcher.exit
|
44
|
+
@dispatcher.report(tests)
|
45
|
+
end
|
46
|
+
def parse test_identifier=nil
|
47
|
+
specs=[]
|
48
|
+
#so, while we are parsing, we have a list of tests
|
49
|
+
#we're either parsing all of the tests, or just one
|
50
|
+
#make sure the one test is on the list
|
51
|
+
if test_identifier
|
52
|
+
if @configuration.tests.include?(File.expand_path(test_identifier))
|
53
|
+
specs<<parse_specification(File.expand_path(test_identifier))
|
54
|
+
else
|
55
|
+
error(File.expand_path(test_identifier),"Does not exist in the configuration")
|
56
|
+
end
|
57
|
+
else
|
58
|
+
specs=parse_specifications(@configuration.tests)
|
59
|
+
end
|
60
|
+
specs.compact!
|
61
|
+
check,setup,teardown=parse_specials(@configuration)
|
62
|
+
return [check,setup,teardown,specs]
|
63
|
+
end
|
64
|
+
private
|
65
|
+
def parse_specifications tests
|
66
|
+
tests.map{|t| parse_specification(t)}.compact
|
67
|
+
end
|
68
|
+
def parse_specification spec_identifier
|
69
|
+
begin
|
70
|
+
@parser.parse_specification(spec_identifier)
|
71
|
+
rescue Rutema::ParserError
|
72
|
+
error(spec_identifier,$!.message)
|
73
|
+
nil
|
74
|
+
end
|
75
|
+
end
|
76
|
+
def parse_specials configuration
|
77
|
+
check=nil
|
78
|
+
setup=nil
|
79
|
+
teardown=nil
|
80
|
+
if configuration.check
|
81
|
+
check=parse_specification(configuration.check)
|
82
|
+
end
|
83
|
+
if configuration.setup
|
84
|
+
setup=parse_specification(configuration.setup)
|
85
|
+
end
|
86
|
+
if configuration.teardown
|
87
|
+
teardown=parse_specification(configuration.teardown)
|
88
|
+
end
|
89
|
+
return check,setup,teardown
|
90
|
+
end
|
91
|
+
def run_scenarios specs,check
|
92
|
+
if specs.empty?
|
93
|
+
error(nil,"No tests to run")
|
94
|
+
else
|
95
|
+
if check
|
96
|
+
if run_test(check)==:success
|
97
|
+
specs.each{|s| run_test(s)}
|
98
|
+
else
|
99
|
+
error(nil,"Check test failed")
|
100
|
+
end
|
101
|
+
else
|
102
|
+
specs.each{|spec| run_test(spec)}
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
def run_test specification
|
107
|
+
if specification.scenario
|
108
|
+
status=@runner.run(specification)["status"]
|
109
|
+
else
|
110
|
+
status=:not_executed
|
111
|
+
message(:test=>specification.name,:text=>"No scenario", :status=>status)
|
112
|
+
end
|
113
|
+
return status
|
114
|
+
end
|
115
|
+
def instantiate_class definition,configuration
|
116
|
+
if definition[:class]
|
117
|
+
klass=definition[:class]
|
118
|
+
return klass.new(configuration)
|
119
|
+
end
|
120
|
+
return nil
|
121
|
+
end
|
122
|
+
end
|
123
|
+
class Dispatcher
|
124
|
+
INTERVAL=0.01
|
125
|
+
def initialize queue,configuration
|
126
|
+
@queue = queue
|
127
|
+
@queues = {}
|
128
|
+
@streaming_reporters=[]
|
129
|
+
@block_reporters=[]
|
130
|
+
@collector=Rutema::Reporters::Collector.new(nil,self)
|
131
|
+
if configuration.reporters
|
132
|
+
instances=configuration.reporters.values.map{|v| instantiate_reporter(v,configuration) if v[:class] != Reporters::Summary}.compact
|
133
|
+
@streaming_reporters,_=instances.partition{|rep| rep.respond_to?(:update)}
|
134
|
+
@block_reporters,_=instances.partition{|rep| rep.respond_to?(:report)}
|
135
|
+
end
|
136
|
+
@streaming_reporters<<@collector
|
137
|
+
@configuration=configuration
|
138
|
+
end
|
139
|
+
def subscribe identifier
|
140
|
+
@queues[identifier]=Queue.new
|
141
|
+
return @queues[identifier]
|
142
|
+
end
|
143
|
+
|
144
|
+
def run!
|
145
|
+
puts "Running #{@streaming_reporters.size} streaming reporters" if $DEBUG
|
146
|
+
@streaming_reporters.each {|r| r.run!}
|
147
|
+
@thread=Thread.new do
|
148
|
+
while true do
|
149
|
+
dispatch()
|
150
|
+
sleep INTERVAL
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
def report specs
|
156
|
+
@block_reporters.each do |r|
|
157
|
+
r.report(specs,@collector.states,@collector.errors)
|
158
|
+
end
|
159
|
+
Reporters::Summary.new(@configuration,self).report(specs,@collector.states,@collector.errors)
|
160
|
+
end
|
161
|
+
def exit
|
162
|
+
puts "Exiting main dispatcher" if $DEBUG
|
163
|
+
if @thread
|
164
|
+
flush
|
165
|
+
@streaming_reporters.each {|r| r.exit}
|
166
|
+
Thread.kill(@thread)
|
167
|
+
end
|
168
|
+
end
|
169
|
+
private
|
170
|
+
def flush
|
171
|
+
puts "Flushing queues" if $DEBUG
|
172
|
+
if @thread
|
173
|
+
while @queue.size>0 do
|
174
|
+
dispatch()
|
175
|
+
sleep INTERVAL
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
def instantiate_reporter definition,configuration
|
180
|
+
if definition[:class]
|
181
|
+
klass=definition[:class]
|
182
|
+
return klass.new(configuration,self)
|
183
|
+
end
|
184
|
+
return nil
|
185
|
+
end
|
186
|
+
def dispatch
|
187
|
+
if @queue.size>0
|
188
|
+
data=@queue.pop
|
189
|
+
@queues.each{ |i,q| q.push(data) } if data
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
186
193
|
end
|
@@ -1,28 +1,89 @@
|
|
1
|
-
module Rutema
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
@
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
1
|
+
module Rutema
|
2
|
+
#Represents the data beeing shunted between the components in lieu of logging.
|
3
|
+
#
|
4
|
+
#This is the primary type passed to the event reporters
|
5
|
+
class Message
|
6
|
+
attr_accessor :test,:text,:timestamp
|
7
|
+
#Keys used:
|
8
|
+
# test - the test id/name
|
9
|
+
# text - the text of the message
|
10
|
+
# timestamp
|
11
|
+
def initialize params
|
12
|
+
@test=params.fetch(:test,"")
|
13
|
+
@text=params.fetch(:text,"")
|
14
|
+
@timestamp=params.fetch(:timestamp,0)
|
15
|
+
end
|
16
|
+
|
17
|
+
def to_s
|
18
|
+
msg=""
|
19
|
+
msg<<"#{@test} " unless @test.empty?
|
20
|
+
msg<<@text
|
21
|
+
return msg
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class ErrorMessage<Message
|
26
|
+
def to_s
|
27
|
+
msg="ERROR - "
|
28
|
+
msg<<"#{@test} " unless @test.empty?
|
29
|
+
msg<<@text
|
30
|
+
return msg
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class RunnerMessage<Message
|
35
|
+
attr_accessor :duration,:status,:number,:out,:err
|
36
|
+
def initialize params
|
37
|
+
super(params)
|
38
|
+
@duration=params.fetch("duration",0)
|
39
|
+
@status=params.fetch("status",:none)
|
40
|
+
@number=params.fetch("number",1)
|
41
|
+
@out=params.fetch("out","")
|
42
|
+
@err=params.fetch("err","")
|
43
|
+
end
|
44
|
+
|
45
|
+
def to_s
|
46
|
+
msg="#{@test}:"
|
47
|
+
msg<<"#{@text}." unless @text.empty?
|
48
|
+
outpt=output()
|
49
|
+
msg<<" Output:\n#{outpt}" unless outpt.empty? || @status!=:error
|
50
|
+
return msg
|
51
|
+
end
|
52
|
+
|
53
|
+
def output
|
54
|
+
msg=""
|
55
|
+
msg<<"#{@out}\n" unless @out.empty?
|
56
|
+
msg<<@err unless @err.empty?
|
57
|
+
return msg.chomp
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
module Messaging
|
62
|
+
def error identifier,message
|
63
|
+
@queue.push(ErrorMessage.new(:test=>identifier,:text=>message,:timestamp=>Time.now))
|
64
|
+
end
|
65
|
+
def message message
|
66
|
+
case message
|
67
|
+
when String
|
68
|
+
Message.new(:text=>message,:timestamp=>Time.now)
|
69
|
+
when Hash
|
70
|
+
hm=Message.new(message)
|
71
|
+
hm=RunnerMessage.new(message) if message[:test] && message["status"]
|
72
|
+
hm.timestamp=Time.now
|
73
|
+
@queue.push(hm)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
#Generic error class for errors in the engine
|
78
|
+
class RutemaError<RuntimeError
|
79
|
+
end
|
80
|
+
#Is raised when an error is found in a specification
|
81
|
+
class ParserError<RutemaError
|
82
|
+
end
|
83
|
+
#Is raised on an unexpected error during execution
|
84
|
+
class RunnerError<RutemaError
|
85
|
+
end
|
86
|
+
#Errors in reporters should use this class
|
87
|
+
class ReportError<RutemaError
|
88
|
+
end
|
28
89
|
end
|
File without changes
|
data/lib/rutema/core/parser.rb
CHANGED
@@ -1,35 +1,35 @@
|
|
1
|
-
# Copyright (c) 2007-2015 Vassilis Rizopoulos. All rights reserved.
|
2
|
-
require_relative 'framework'
|
3
|
-
|
4
|
-
module Rutema
|
5
|
-
module Parsers
|
6
|
-
#Base class that bombs out when used.
|
7
|
-
#
|
8
|
-
#Derive your parser class from this class and implement parse_specification and validate_configuration
|
9
|
-
class SpecificationParser
|
10
|
-
attr_reader :configuration
|
11
|
-
def initialize configuration
|
12
|
-
@configuration=configuration
|
13
|
-
@configuration||={}
|
14
|
-
validate_configuration
|
15
|
-
end
|
16
|
-
#parses a specification
|
17
|
-
def parse_specification param
|
18
|
-
raise ParserError,"not implemented. You should derive a parser implementation from SpecificationParser!"
|
19
|
-
end
|
20
|
-
#parses the setup script. By default calls parse_specification
|
21
|
-
def parse_setup param
|
22
|
-
parse_specification(param)
|
23
|
-
end
|
24
|
-
#parses the teardown script. By default calls parse_specification
|
25
|
-
def parse_teardown param
|
26
|
-
parse_specification(param)
|
27
|
-
end
|
28
|
-
#The parser stores it's configuration in @configuration
|
29
|
-
#
|
30
|
-
#To avoid validating the configuration in element_* methods repeatedly, do all configuration validation here
|
31
|
-
def validate_configuration
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
1
|
+
# Copyright (c) 2007-2015 Vassilis Rizopoulos. All rights reserved.
|
2
|
+
require_relative 'framework'
|
3
|
+
|
4
|
+
module Rutema
|
5
|
+
module Parsers
|
6
|
+
#Base class that bombs out when used.
|
7
|
+
#
|
8
|
+
#Derive your parser class from this class and implement parse_specification and validate_configuration
|
9
|
+
class SpecificationParser
|
10
|
+
attr_reader :configuration
|
11
|
+
def initialize configuration
|
12
|
+
@configuration=configuration
|
13
|
+
@configuration||={}
|
14
|
+
validate_configuration
|
15
|
+
end
|
16
|
+
#parses a specification
|
17
|
+
def parse_specification param
|
18
|
+
raise ParserError,"not implemented. You should derive a parser implementation from SpecificationParser!"
|
19
|
+
end
|
20
|
+
#parses the setup script. By default calls parse_specification
|
21
|
+
def parse_setup param
|
22
|
+
parse_specification(param)
|
23
|
+
end
|
24
|
+
#parses the teardown script. By default calls parse_specification
|
25
|
+
def parse_teardown param
|
26
|
+
parse_specification(param)
|
27
|
+
end
|
28
|
+
#The parser stores it's configuration in @configuration
|
29
|
+
#
|
30
|
+
#To avoid validating the configuration in element_* methods repeatedly, do all configuration validation here
|
31
|
+
def validate_configuration
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
35
|
end
|