rutema 1.3.0 → 2.0.0.pre

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. checksums.yaml +7 -0
  2. data/History.txt +204 -194
  3. data/Manifest.txt +15 -48
  4. data/README.md +55 -61
  5. data/bin/rutema +7 -7
  6. data/lib/rutema/application.rb +61 -0
  7. data/lib/rutema/core/configuration.rb +195 -0
  8. data/lib/rutema/core/engine.rb +186 -0
  9. data/lib/rutema/core/framework.rb +28 -0
  10. data/lib/rutema/{objectmodel.rb → core/objectmodel.rb} +43 -48
  11. data/lib/rutema/core/parser.rb +35 -0
  12. data/lib/rutema/core/reporter.rb +105 -0
  13. data/lib/rutema/core/runner.rb +83 -0
  14. data/lib/rutema/elements/minimal.rb +47 -44
  15. data/lib/rutema/parsers/xml.rb +154 -186
  16. data/lib/rutema/version.rb +9 -0
  17. metadata +39 -108
  18. data/README.txt +0 -44
  19. data/Rakefile +0 -30
  20. data/examples/README.md +0 -17
  21. data/examples/config/database.rutema +0 -17
  22. data/examples/config/full.rutema +0 -27
  23. data/examples/config/minimal.rutema +0 -10
  24. data/examples/specs/T001.spec +0 -8
  25. data/examples/specs/T002.spec +0 -8
  26. data/examples/specs/T003.spec +0 -8
  27. data/examples/specs/T004.spec +0 -8
  28. data/examples/specs/T005.spec +0 -10
  29. data/examples/specs/T006.spec +0 -9
  30. data/examples/specs/check.spec +0 -8
  31. data/examples/specs/fail.spec +0 -9
  32. data/examples/specs/include.scenario +0 -5
  33. data/examples/specs/rutema.spec +0 -10
  34. data/examples/specs/setup.spec +0 -8
  35. data/examples/specs/teardown.spec +0 -8
  36. data/lib/rutema/configuration.rb +0 -173
  37. data/lib/rutema/models/activerecord.rb +0 -159
  38. data/lib/rutema/models/base.rb +0 -5
  39. data/lib/rutema/parsers/base.rb +0 -45
  40. data/lib/rutema/rake.rb +0 -62
  41. data/lib/rutema/reporters/activerecord.rb +0 -82
  42. data/lib/rutema/reporters/base.rb +0 -23
  43. data/lib/rutema/reporters/email.rb +0 -84
  44. data/lib/rutema/reporters/text.rb +0 -77
  45. data/lib/rutema/runners/default.rb +0 -157
  46. data/lib/rutema/runners/step.rb +0 -23
  47. data/lib/rutema/system.rb +0 -302
  48. data/test/data/duplicate_name.spec +0 -8
  49. data/test/data/no_title.spec +0 -5
  50. data/test/data/sample.spec +0 -8
  51. data/test/data/test_identifiers.rutema +0 -7
  52. data/test/test_activerecord.rb +0 -0
  53. data/test/test_configuration.rb +0 -43
  54. data/test/test_objectmodel.rb +0 -82
  55. data/test/test_parsers.rb +0 -131
  56. data/test/test_reporters.rb +0 -115
  57. data/test/test_runners.rb +0 -70
  58. data/test/test_system.rb +0 -45
@@ -1,77 +0,0 @@
1
- # Copyright (c) 2007-2010 Vassilis Rizopoulos. All rights reserved.
2
-
3
- module Rutema
4
- #This reporter creates a simple text summary of a test run
5
- #
6
- #The following configuration keys are used by TextReporter:
7
- #
8
- #:verbose - when true, the report contains info on setup and teardown specs. Optional. Default is false
9
- class TextReporter
10
- def initialize params=nil
11
- @verbose=params[:verbose] if params
12
- @verbose||=false
13
- end
14
- #Returns the text summary
15
- #
16
- #runner_states is an Array of Patir::CommandSequenceStatus containing the stati of the last run (so it contains all the Scenario stati for the loaded tests)
17
- #
18
- #parse_errors is an Array of {:filename,:error} hashes containing the errors encountered by the parser when loading the specifications
19
- def report specifications,runner_states,parse_errors,configuration
20
- return text_report(specifications,runner_states,parse_errors)
21
- end
22
- private
23
- def text_report specifications,runner_states,parse_errors
24
- msg=""
25
- #Report on parse errors
26
- msg<<"No parse errors" if parse_errors.empty?
27
- msg<<"One parse error:" if parse_errors.size==1
28
- msg<<"#{parse_errors.size} parse errors:" if parse_errors.size>1
29
- parse_errors.each do |er|
30
- msg<<"\n\tin #{er[:filename]} : #{er[:error]}"
31
- end
32
- msg<<"\n---"
33
- #Report on scenarios
34
- runner_states.compact!#make sure no nil elements make it through
35
- msg<<"\nNo scenarios in this run" if runner_states.empty?
36
- if @verbose
37
- states=runner_states
38
- else
39
- states=runner_states.select{|state| state.sequence_name !~ /[_setup|_teardown]$/}
40
- end
41
- msg<<"\nOne scenario in the current run" if states.size==1
42
- msg<<"\n#{states.size} scenarios in the current run" if states.size>1
43
-
44
- not_run = states.select{|state| state.status == :not_executed }.sort_by {|state| state.sequence_id.to_i}
45
- errors = states.select{|state| state.status == :error }.sort_by {|state| state.sequence_id.to_i}
46
- warnings = states.select{|state| state.status == :warning }.sort_by {|state| state.sequence_id.to_i}
47
- successes = states.select{|state| state.status == :success }.sort_by {|state| state.sequence_id.to_i}
48
- msg<<"\n#{errors.size} errors, #{warnings.size} warnings, #{successes.size} successes, #{not_run.size} not executed (setup failure)"
49
- msg<<"\nErrors:" unless errors.empty?
50
- msg<<scenario_summaries(errors,specifications)
51
- msg<<"\nWarnings:" unless warnings.empty?
52
- msg<<scenario_summaries(warnings,specifications)
53
- msg<<"\nNot executed:" unless not_run.empty?
54
- not_run.each do |state|
55
- if specifications[state.sequence_name]
56
- msg<<"\n#{specifications[state.sequence_name].title}"
57
- else
58
- msg<<"\n#{state.sequence_name}"
59
- end
60
- end
61
- msg<<"\nSuccesses:" unless successes.empty?
62
- msg<<scenario_summaries(successes,specifications)
63
- return msg
64
- end
65
-
66
- def scenario_summaries scenarios,specifications
67
- msg=""
68
- unless scenarios.empty?
69
- scenarios.each do |state|
70
- msg<<"\n#{specifications[state.sequence_name].title}" if specifications[state.sequence_name]
71
- msg<<"\n#{state.summary}\n---"
72
- end
73
- end
74
- return msg
75
- end
76
- end
77
- end
@@ -1,157 +0,0 @@
1
- # Copyright (c) 2007-2011 Vassilis Rizopoulos. All rights reserved.
2
- $:.unshift File.join(File.dirname(__FILE__),'..','..')
3
-
4
- module Rutema
5
- #Runner executes TestScenario instances and maintains the state of all scenarios run.
6
- class Runner
7
- attr_reader :states,:number_of_runs,:context
8
- attr_accessor :setup,:teardown
9
- attr_writer :attended
10
-
11
- #setup and teardown are TestScenario instances that will run before and after each call
12
- #to the scenario.
13
- def initialize context=nil,setup=nil, teardown=nil,logger=nil
14
- @setup=setup
15
- @teardown=teardown
16
- @attended=false
17
- @logger=logger
18
- @logger||=Patir.setup_logger
19
- @states=Hash.new
20
- @number_of_runs=0
21
- @context=context || Hash.new
22
- end
23
-
24
- #Tells you if the system runs in the mode that expects user input
25
- def attended?
26
- return @attended
27
- end
28
- #Runs a scenario and stores the result internally
29
- #
30
- #Returns the result of the run as a Patir::CommandSequenceStatus
31
- def run name,scenario, run_setup=true
32
- @logger.debug("Starting run for #{name} with #{scenario.inspect}")
33
- @context[:scenario_name]=name
34
- #if setup /teardown is defined we need to execute them before and after
35
- if @setup && run_setup
36
- @logger.info("Setup for #{name}")
37
- @states["#{name}_setup"]=run_scenario("#{name}_setup",@setup)
38
- @states["#{name}_setup"].sequence_id="s#{@number_of_runs}"
39
- if @states["#{name}_setup"].executed?
40
- #do not execute the scenario unless the setup was succesful
41
- if @states["#{name}_setup"].success?
42
- @logger.info("Scenario for #{name}")
43
- @states[name]=run_scenario(name,scenario)
44
- @states[name].sequence_id="#{@number_of_runs}"
45
- else
46
- @states[name]=initialize_state(name,scenario)
47
- @states[name].sequence_id="#{@number_of_runs}"
48
- end
49
- end
50
- else
51
- @logger.info("Scenario for #{name}")
52
- @states[name]=run_scenario(name,scenario)
53
- @states[name].sequence_id="#{@number_of_runs}"
54
- end
55
- #no setup means no teardown
56
- if @teardown && run_setup
57
- #always execute teardown
58
- @logger.warn("Teardown for #{name}")
59
- @states["#{name}_teardown"]=run_scenario("#{name}_teardown",@teardown)
60
- @states["#{name}_teardown"].sequence_id="#{@number_of_runs}t"
61
- end
62
- @number_of_runs+=1
63
- @context[:scenario_name]=nil
64
- return @states[name]
65
- end
66
-
67
- #Returns the state of the scenario with the given name.
68
- #
69
- #Will return nil if no scenario is found under that name.
70
- def [](name)
71
- return @states[name]
72
- end
73
-
74
- #Resets the Runner's internal state
75
- def reset
76
- @states.clear
77
- @number_of_runs=0
78
- end
79
-
80
- #returns true if all the scenarios in the last run were succesful or if nothing was run yet
81
- def success?
82
- @success=true
83
- @states.each do |k,v|
84
- @success&=(v.status!=:error)
85
- end
86
- return @success
87
- end
88
- private
89
- def run_scenario name,scenario
90
- state=initialize_state(name,scenario)
91
- begin
92
- if evaluate_attention(scenario,state)
93
- stps=scenario.steps
94
- if stps.empty?
95
- @logger.warn("Scenario #{name} contains no steps")
96
- state.status=:warning
97
- else
98
- stps.each do |s|
99
- state.step=run_step(s)
100
- break if :error==state.status
101
- end
102
- end
103
- end
104
- rescue
105
- @logger.error("Encountered error in #{name}: #{$!.message}")
106
- @logger.debug($!)
107
- state.status=:error
108
- end
109
- state.stop_time=Time.now
110
- state.sequence_id=@number_of_runs
111
- return state
112
- end
113
- def initialize_state name,scenario
114
- state=Patir::CommandSequenceStatus.new(name,scenario.steps)
115
- end
116
- def evaluate_attention scenario,state
117
- if scenario.attended?
118
- if !self.attended?
119
- @logger.warn("Attended scenario cannot be run in unattended mode")
120
- state.status=:warning
121
- return false
122
- end
123
- state.strategy=:attended
124
- else
125
- state.strategy=:unattended
126
- end
127
- return true
128
- end
129
- def run_step step
130
- @logger.info("Running step #{step.number} - #{step.name}")
131
- if step.has_cmd? && step.cmd.respond_to?(:run)
132
- step.cmd.run(@context)
133
- msg=step.to_s
134
- if !step.cmd.success?
135
- msg<<"\n#{step.cmd.output}" unless step.cmd.output.empty?
136
- msg<<"\n#{step.cmd.error}" unless step.cmd.error.empty?
137
- end
138
- else
139
- @logger.warn("No command associated with step '#{step.step_type}'. Step number is #{step.number}")
140
- end
141
- step.status=:success if step.status==:error && step.ignore?
142
- log_step_result(step,msg)
143
- return step
144
- end
145
- def log_step_result step,msg
146
- if step.status==:error
147
- if step.ignore?
148
- @logger.warn("Step failed but result is being ignored!\n#{msg}")
149
- else
150
- @logger.error(msg)
151
- end
152
- else
153
- @logger.info(msg) if msg && !msg.empty?
154
- end
155
- end
156
- end
157
- end
@@ -1,23 +0,0 @@
1
- # Copyright (c) 2007-2011 Vassilis Rizopoulos. All rights reserved.
2
- $:.unshift File.join(File.dirname(__FILE__),'..','..')
3
-
4
- require 'rutema/runners/default'
5
-
6
- module Rutema
7
- #StepRunner halts before every step and asks if it should be executed or not.
8
- class StepRunner<Runner
9
- def initialize setup=nil, teardown=nil,logger=nil
10
- @questioner=HighLine.new
11
- super(setup,teardown,logger)
12
- end
13
- def run_step step
14
- if @questioner.agree("Execute #{step.to_s}?")
15
- return super(step)
16
- else
17
- msg="#{step.number} - #{step.step_type} - #{step.status}"
18
- @logger.info(msg)
19
- return step
20
- end
21
- end
22
- end
23
- end
data/lib/rutema/system.rb DELETED
@@ -1,302 +0,0 @@
1
- # Copyright (c) 2007-2012 Vassilis Rizopoulos. All rights reserved.
2
- $:.unshift File.join(File.dirname(__FILE__),"..")
3
- require 'patir/command'
4
- require 'patir/base'
5
- require 'rutema/configuration'
6
-
7
- require 'rutema/parsers/base'
8
-
9
- require 'rutema/runners/default'
10
- require 'rutema/runners/step'
11
-
12
- require 'rutema/reporters/text'
13
-
14
- module Rutema
15
- #This module defines the version numbers for the library
16
- module Version
17
- MAJOR=1
18
- MINOR=3
19
- TINY=0
20
- STRING=[ MAJOR, MINOR, TINY ].join( "." )
21
- end
22
- #This class coordinates parsing, execution and reporting of test specifications
23
- class Coordinator
24
- attr_accessor :configuration,:parse_errors,:parsed_files
25
- attr_reader :test_states
26
- def initialize configuration,logger=nil
27
- @logger=logger
28
- @logger||=Patir.setup_logger
29
- @parse_errors=Array.new
30
- @configuration=configuration
31
- @parser=instantiate_class(@configuration.parser)
32
- raise "Could not instantiate parser" unless @parser
33
- @reporters=@configuration.reporters.collect{ |reporter| instantiate_class(reporter) }
34
- @reporters.compact!
35
- #this will hold any specifications that are succesfully parsed.
36
- @specifications=Hash.new
37
- @parsed_files=Array.new
38
- @test_states=Hash.new
39
- end
40
- #Runs a set of tests
41
- #
42
- #mode can be :all, :attended, :unattended or a test filename
43
- def run mode
44
- @runner||=create_runner
45
- @configuration.context[:start_time]=Time.now
46
- @logger.info("Run started in mode '#{mode}'")
47
- begin
48
- case mode
49
- when :all
50
- @runner.attended=true
51
- specs=parse_all_specifications
52
- run_scenarios(specs)
53
- when :attended
54
- @runner.attended=true
55
- specs=parse_all_specifications
56
- run_scenarios(specs.select{|s| s.scenario && s.scenario.attended?})
57
- when :unattended
58
- specs=parse_all_specifications
59
- run_scenarios(specs.select{|s| s.scenario && !s.scenario.attended?})
60
- when String
61
- @runner.attended=true
62
- spec=parse_specification(mode)
63
- run_test(spec) if spec
64
- else
65
- @logger.fatal("Don't know how to run '#{mode}'")
66
- raise "Don't know how to run '#{mode}'"
67
- end
68
- rescue
69
- @logger.debug($!)
70
- @logger.fatal("Runner error: #{$!.message}")
71
- raise
72
- end
73
- @configuration.context[:end_time]=Time.now
74
- @logger.info("Run completed in #{@configuration.context[:end_time]-@configuration.context[:start_time]}s")
75
- end
76
-
77
- #Parses all specification files defined in the configuration
78
- def parse_all_specifications
79
- @configuration.tests.collect do |t|
80
- begin
81
- spec=parse_specification(t)
82
- rescue
83
- @logger.debug($!)
84
- @logger.error($!.message)
85
- end
86
- end.compact
87
- end
88
- #Delegates reporting to all configured reporters spawning one thread per reporter
89
- #
90
- #It then joins the threads and returns when all of them are finished.
91
- def report
92
- #get the states from the runner
93
- @runner.states.each do |k,v|
94
- if v
95
- @test_states[k]=v
96
- else
97
- @logger.warn("State for #{k} is nil")
98
- end
99
- end
100
- threads=Array.new
101
- #get the runner stati and the configuration and give it to the reporters
102
- @reporters.each do |reporter|
103
- threads<<Thread.new(reporter,@specifications,@test_states.values,@parse_errors,@configuration) do |reporter,specs,status,perrors,configuration|
104
- begin
105
- @logger.debug(reporter.report(specs,status,perrors,configuration))
106
- rescue RuntimeError
107
- @logger.error("Error in #{reporter.class}: #{$!.message}")
108
- @logger.debug($!)
109
- end
110
- end
111
- end
112
- threads.each do |t|
113
- @logger.debug("Joining #{t}")
114
- t.join
115
- end
116
- end
117
-
118
- #returns true if all scenarios in the last run were succesful
119
- def last_run_a_success?
120
- return @runner.success?
121
- end
122
- def to_s#:nodoc:
123
- "Parsed #{@parsed_files.size} files\n#{TextReporter.new.report(@specifications,@runner.states.values,@parse_errors,@configuration)}"
124
- end
125
- private
126
- def instantiate_class definition
127
- if definition[:class]
128
- #add the logger to the definition
129
- definition[:logger]=@logger
130
- #add the configuration to the definition
131
- definition[:configuration]=@configuration
132
- klass=definition[:class]
133
- return klass.new(definition)
134
- end
135
- return nil
136
- end
137
-
138
- def create_runner
139
- setup=nil
140
- teardown=nil
141
- if @configuration.setup
142
- @logger.info("Parsing setup specification from '#{@configuration.setup}'")
143
- setup=@parser.parse_setup(@configuration.setup).scenario
144
- end
145
- if @configuration.teardown
146
- @logger.info("Parsing teardown specification from '#{@configuration.teardown}'")
147
- teardown=@parser.parse_teardown(@configuration.teardown).scenario
148
- end
149
- if @configuration.use_step_by_step
150
- @logger.info("Using StepRunner")
151
- return StepRunner.new(@configuration.context,setup,teardown,@logger)
152
- else
153
- return Runner.new(@configuration.context,setup,teardown,@logger)
154
- end
155
- end
156
-
157
- def parse_specification spec_identifier
158
- spec=nil
159
- begin
160
- @parsed_files<<spec_identifier
161
- @parsed_files.uniq!
162
- spec=@parser.parse_specification(spec_identifier)
163
- if @specifications[spec.name]
164
- msg="Duplicate specification name '#{spec.name}' in '#{spec_identifier}'"
165
- @logger.error(msg)
166
- @parse_errors<<{:filename=>spec_identifier,:error=>msg}
167
- @test_states[spec.name]=Patir::CommandSequenceStatus.new(spec.name,[])
168
- else
169
- @specifications[spec.name]=spec
170
- @test_states[spec.name]=Patir::CommandSequenceStatus.new(spec.name,spec.scenario.steps)
171
- end
172
- rescue ParserError
173
- @logger.error("Error parsing '#{spec_identifier}': #{$!.message}")
174
- @parse_errors<<{:filename=>spec_identifier,:error=>$!.message}
175
- end
176
- return spec
177
- end
178
-
179
- def run_scenarios specs
180
- specs.compact!
181
- if specs.empty?
182
- @logger.error("No tests to run")
183
- else
184
- if @configuration.check
185
- @logger.info("Parsing check test '#{@configuration.check}'")
186
- spec=parse_specification(@configuration.check)
187
- if spec
188
- @logger.info("Running check test '#{spec.to_s}'")
189
- if run_test(spec,false).success?
190
- specs.each{|s| run_test(s)}
191
- else
192
- @logger.error("Check test failed")
193
- end
194
- else
195
- @logger.error("Error parsing check test")
196
- end
197
- else
198
- specs.each{|s| run_test(s)}
199
- end
200
- end
201
- end
202
-
203
- def run_test specification,run_setup=true
204
- @logger.info("Running #{specification.name} - #{specification.title}")
205
- if specification.scenario
206
- status=@runner.run(specification.name,specification.scenario,run_setup)
207
- else
208
- @logger.warn("#{specification.name} has no scenario")
209
- status=:not_executed
210
- end
211
- @test_states[specification.name]=status
212
- return status
213
- end
214
- end
215
- #The "executioner" application class
216
- #
217
- #Parses the commandline, sets up the configuration and launches Cordinator
218
- class RutemaX
219
- require 'optparse'
220
- def initialize command_line_args
221
- parse_command_line(command_line_args)
222
- @logger=Patir.setup_logger(@log_file)
223
- @logger.info("rutema v#{Version::STRING}")
224
- begin
225
- raise "No configuration file defined!" if !@config_file
226
- @configuration=RutemaConfigurator.new(@config_file,@logger).configuration
227
- @configuration.context[:config_file]=File.basename(@config_file)
228
- @configuration.use_step_by_step=@step
229
- Dir.chdir(File.dirname(@config_file)) do
230
- @coordinator=Coordinator.new(@configuration,@logger)
231
- application_flow
232
- end
233
- rescue Patir::ConfigurationException
234
- @logger.debug($!)
235
- @logger.fatal("Configuration error '#{$!.message}'")
236
- raise "Configuration error '#{$!.message}'"
237
- rescue
238
- @logger.debug($!)
239
- @logger.fatal("#{$!.message}")
240
- raise
241
- end
242
- end
243
- private
244
- def parse_command_line args
245
- args.options do |opt|
246
- opt.on("Options:")
247
- opt.on("--debug", "-d","Turns on debug messages") { $DEBUG=true }
248
- opt.on("--config FILE", "-c FILE",String,"Loads the configuration from FILE") { |config_file| @config_file=config_file}
249
- opt.on("--log FILE", "-l FILE",String,"Redirects the log output to FILE") { |log_file| @log_file=logfile}
250
- opt.on("--check","Runs just the check test"){@check=true}
251
- opt.on("--step","Runs test cases step by step"){@step=true}
252
- opt.on("-v", "--version","Displays the version") { $stdout.puts("rutema v#{Version::STRING}");exit 0 }
253
- opt.on("--help", "-h", "-?", "This text") { $stdout.puts opt; exit 0 }
254
- opt.on("The commands are:")
255
- opt.on("\tall - Runs all tests")
256
- opt.on("\tattended - Runs all attended tests")
257
- opt.on("\tunattended - Runs all unattended tests")
258
- opt.on("You can also provide a specification filename in order to run a single test")
259
- opt.parse!
260
- #and now the rest
261
- if args.empty?
262
- @mode=:unattended
263
- else
264
- command=args.shift
265
- case command
266
- when "attended"
267
- @mode=:attended
268
- when "all"
269
- @mode=:all
270
- when "unattended"
271
- @mode=:unattended
272
- else
273
- @mode=command
274
- end
275
- end
276
- end
277
- end
278
-
279
- def application_flow
280
- if @check
281
- #run just the check test
282
- if @configuration.check
283
- @coordinator.run(@configuration.check)
284
- else
285
- @logger.fatal("There is no check test defined in the configuration.")
286
- raise "There is no check test defined in the configuration."
287
- end
288
- else
289
- #run everything
290
- @coordinator.run(@mode)
291
- end
292
- @logger.info("Report:\n#{@coordinator.to_s}")
293
- @coordinator.report
294
- if @coordinator.parse_errors.empty? && @coordinator.last_run_a_success?
295
- @logger.info("All tests successful")
296
- else
297
- @logger.warn("Not all tests were successful")
298
- raise "Not all tests were successful"
299
- end
300
- end
301
- end
302
- end