rutema 2.0.0.pre5 → 2.0.0.pre6

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
- @runner=instantiate_class(configuration.runner,configuration)
16
- else
17
- @runner=Rutema::Runners::Default.new(configuration.context,@queue)
18
- end
19
- raise RutemaError,"Could not instantiate parser" unless @parser
20
- @dispatcher=Dispatcher.new(@queue,configuration)
21
- @configuration=configuration
22
- end
23
- def run test_identifier=nil
24
- @dispatcher.run!
25
- #start
26
- message("start")
27
- check,setup,teardown,tests=*parse(test_identifier)
28
- if tests.empty?
29
- @dispatcher.exit
30
- raise RutemaError,"Did not parse any tests succesfully"
31
- else
32
- @runner.setup=setup
33
- @runner.teardown=teardown
34
- #running - at this point we've done any and all checks and we're stepping on the gas
35
- message("running")
36
- run_scenarios(tests,check)
37
- end
38
- message("end")
39
- @dispatcher.exit
40
- @dispatcher.report(tests)
41
- end
42
- def parse test_identifier=nil
43
- specs=[]
44
- #so, while we are parsing, we have a list of tests
45
- #we're either parsing all of the tests, or just one
46
- #make sure the one test is on the list
47
- if test_identifier
48
- if @configuration.tests.include?(File.expand_path(test_identifier))
49
- specs<<parse_specification(File.expand_path(test_identifier))
50
- else
51
- error(File.expand_path(test_identifier),"Does not exist in the configuration")
52
- end
53
- else
54
- specs=parse_specifications(@configuration.tests)
55
- end
56
- specs.compact!
57
- check,setup,teardown=parse_specials(@configuration)
58
- return [check,setup,teardown,specs]
59
- end
60
- private
61
- def parse_specifications tests
62
- tests.map{|t| parse_specification(t)}.compact
63
- end
64
- def parse_specification spec_identifier
65
- begin
66
- @parser.parse_specification(spec_identifier)
67
- rescue Rutema::ParserError
68
- error(spec_identifier,$!.message)
69
- nil
70
- end
71
- end
72
- def parse_specials configuration
73
- check=nil
74
- setup=nil
75
- teardown=nil
76
- if configuration.check
77
- check=parse_specification(configuration.check)
78
- end
79
- if configuration.setup
80
- setup=parse_specification(configuration.setup)
81
- end
82
- if configuration.teardown
83
- teardown=parse_specification(configuration.teardown)
84
- end
85
- return check,setup,teardown
86
- end
87
- def run_scenarios specs,check
88
- if specs.empty?
89
- error(nil,"No tests to run")
90
- else
91
- if check
92
- if run_test(check)==:success
93
- specs.each{|s| run_test(s)}
94
- else
95
- error(nil,"Check test failed")
96
- end
97
- else
98
- specs.each{|spec| run_test(spec)}
99
- end
100
- end
101
- end
102
- def run_test specification
103
- if specification.scenario
104
- status=@runner.run(specification)["status"]
105
- else
106
- status=:not_executed
107
- message(:test=>specification.name,:message=>"No scenario", :status=>status)
108
- end
109
- return status
110
- end
111
- def instantiate_class definition,configuration
112
- if definition[:class]
113
- klass=definition[:class]
114
- return klass.new(configuration)
115
- end
116
- return nil
117
- end
118
- end
119
- class Dispatcher
120
- INTERVAL=0.01
121
- def initialize queue,configuration
122
- @queue = queue
123
- @queues = {}
124
- @streaming_reporters=[]
125
- @block_reporters=[]
126
- @collector=Rutema::Reporters::Collector.new(nil,self)
127
- if configuration.reporters
128
- instances=configuration.reporters.values.map{|v| instantiate_reporter(v,configuration) if v[:class] != Reporters::Summary}.compact
129
- @streaming_reporters,_=instances.partition{|rep| rep.respond_to?(:update)}
130
- @block_reporters,_=instances.partition{|rep| rep.respond_to?(:report)}
131
- end
132
- @streaming_reporters<<@collector
133
- @configuration=configuration
134
- end
135
- def subscribe identifier
136
- @queues[identifier]=Queue.new
137
- return @queues[identifier]
138
- end
139
-
140
- def run!
141
- @streaming_reporters.each {|r| r.run!}
142
- @thread=Thread.new do
143
- while true do
144
- dispatch()
145
- sleep INTERVAL
146
- end
147
- end
148
- end
149
-
150
- def report specs
151
- @block_reporters.each do |r|
152
- r.report(specs,@collector.states,@collector.errors)
153
- end
154
- Reporters::Summary.new(@configuration,self).report(specs,@collector.states,@collector.errors)
155
- end
156
- def exit
157
- if @thread
158
- flush
159
- @streaming_reporters.each {|r| r.exit}
160
- Thread.kill(@thread)
161
- end
162
- end
163
- private
164
- def flush
165
- if @thread
166
- while @queue.size>0 do
167
- dispatch()
168
- sleep INTERVAL
169
- end
170
- end
171
- end
172
- def instantiate_reporter definition,configuration
173
- if definition[:class]
174
- klass=definition[:class]
175
- return klass.new(configuration,self)
176
- end
177
- return nil
178
- end
179
- def dispatch
180
- if @queue.size>0
181
- data=@queue.pop
182
- @queues.each{ |i,q| q.push(data) } if data
183
- end
184
- end
185
- end
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
- module Messaging
3
- def error identifier,message
4
- message(:test=>identifier,:error=>message)
5
- end
6
- def message message
7
- msg=message
8
- if message.is_a?(String)
9
- msg={:message=>message,:timestamp=>Time.now}
10
- elsif message.is_a?(Hash)
11
- msg[:timestamp]=Time.now
12
- end
13
- @queue.push(msg)
14
- end
15
- end
16
- #Generic error class for errors in the engine
17
- class RutemaError<RuntimeError
18
- end
19
- #Is raised when an error is found in a specification
20
- class ParserError<RutemaError
21
- end
22
- #Is raised on an unexpected error during execution
23
- class RunnerError<RutemaError
24
- end
25
- #Errors in reporters should use this class
26
- class ReportError<RutemaError
27
- end
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
@@ -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