rutema 1.3.0 → 2.0.0.pre

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.
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