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/reporter.rb
CHANGED
@@ -1,129 +1,133 @@
|
|
1
|
-
# Copyright (c) 2007-2015 Vassilis Rizopoulos. All rights reserved.
|
2
|
-
module Rutema
|
3
|
-
#Rutema supports two kinds of reporters.
|
4
|
-
#
|
5
|
-
#Block (from en bloc) reporters receive data via the report() method at the end of a Rutema run
|
6
|
-
#while event reporters receive events continuously during a run via the update() method
|
7
|
-
#
|
8
|
-
#Nothing prevents you from creating a class that implements both behaviours
|
9
|
-
module Reporters
|
10
|
-
class BlockReporter
|
11
|
-
def initialize configuration,dispatcher
|
12
|
-
@configuration=configuration
|
13
|
-
end
|
14
|
-
def report specifications,states,errors
|
15
|
-
end
|
16
|
-
end
|
17
|
-
class EventReporter
|
18
|
-
def initialize configuration,dispatcher
|
19
|
-
@configuration=configuration
|
20
|
-
@queue=dispatcher.subscribe(self.object_id)
|
21
|
-
end
|
22
|
-
|
23
|
-
def run!
|
24
|
-
@thread=Thread.new do
|
25
|
-
while true do
|
26
|
-
if @queue.size>0
|
27
|
-
data=@queue.pop
|
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
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
test_state["
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
end
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
puts
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
def
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
1
|
+
# Copyright (c) 2007-2015 Vassilis Rizopoulos. All rights reserved.
|
2
|
+
module Rutema
|
3
|
+
#Rutema supports two kinds of reporters.
|
4
|
+
#
|
5
|
+
#Block (from en bloc) reporters receive data via the report() method at the end of a Rutema run
|
6
|
+
#while event reporters receive events continuously during a run via the update() method
|
7
|
+
#
|
8
|
+
#Nothing prevents you from creating a class that implements both behaviours
|
9
|
+
module Reporters
|
10
|
+
class BlockReporter
|
11
|
+
def initialize configuration,dispatcher
|
12
|
+
@configuration=configuration
|
13
|
+
end
|
14
|
+
def report specifications,states,errors
|
15
|
+
end
|
16
|
+
end
|
17
|
+
class EventReporter
|
18
|
+
def initialize configuration,dispatcher
|
19
|
+
@configuration=configuration
|
20
|
+
@queue=dispatcher.subscribe(self.object_id)
|
21
|
+
end
|
22
|
+
|
23
|
+
def run!
|
24
|
+
@thread=Thread.new do
|
25
|
+
while true do
|
26
|
+
if @queue.size>0
|
27
|
+
data=@queue.pop
|
28
|
+
begin
|
29
|
+
update(data) if data
|
30
|
+
rescue
|
31
|
+
puts "#{self.class} failed with #{$!.message}"
|
32
|
+
raise
|
33
|
+
end
|
34
|
+
end
|
35
|
+
sleep 0.1
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def update data
|
41
|
+
end
|
42
|
+
|
43
|
+
def exit
|
44
|
+
puts "Exiting #{self.class}" if $DEBUG
|
45
|
+
if @thread
|
46
|
+
puts "Reporter died with #{@queue.size} messages in the queue" unless @thread.alive?
|
47
|
+
while @queue.size>0 && @thread.alive? do
|
48
|
+
sleep 0.1
|
49
|
+
end
|
50
|
+
Thread.kill(@thread)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
class Collector<EventReporter
|
56
|
+
attr_reader :errors,:states
|
57
|
+
def initialize params,dispatcher
|
58
|
+
super(params,dispatcher)
|
59
|
+
@errors=[]
|
60
|
+
@states={}
|
61
|
+
end
|
62
|
+
|
63
|
+
def update message
|
64
|
+
case message
|
65
|
+
when RunnerMessage
|
66
|
+
test_state=@states.fetch(message.test,{})
|
67
|
+
test_state["timestamp"]||=message.timestamp
|
68
|
+
duration=test_state.fetch("duration",0)+message.duration
|
69
|
+
test_state["duration"]=duration
|
70
|
+
test_state["status"]= message.status
|
71
|
+
steps=test_state.fetch("steps",[])
|
72
|
+
steps<<message
|
73
|
+
test_state["steps"]=steps
|
74
|
+
@states[message.test]=test_state
|
75
|
+
when ErrorMessage
|
76
|
+
@errors<<message
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
class Console<EventReporter
|
82
|
+
def initialize configuration,dispatcher
|
83
|
+
super(configuration,dispatcher)
|
84
|
+
@mode=configuration.reporters.fetch(self.class,{})["mode"]
|
85
|
+
end
|
86
|
+
def update message
|
87
|
+
unless @mode=="off"
|
88
|
+
case message
|
89
|
+
when RunnerMessage
|
90
|
+
if message.status == :error
|
91
|
+
puts "FATAL|#{message.to_s}"
|
92
|
+
else
|
93
|
+
puts message.to_s if @mode=="verbose"
|
94
|
+
end
|
95
|
+
when ErrorMessage
|
96
|
+
puts message.to_s
|
97
|
+
when Message
|
98
|
+
puts message.to_s if @mode=="verbose"
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
class Summary<BlockReporter
|
105
|
+
def initialize configuration,dispatcher
|
106
|
+
super(configuration,dispatcher)
|
107
|
+
@silent=configuration.reporters.fetch(self.class,{})["silent"]
|
108
|
+
end
|
109
|
+
def report specs,states,errors
|
110
|
+
failures=[]
|
111
|
+
states.each do |k,v|
|
112
|
+
failures<<k if v.fetch("steps",[]).last.status==:error
|
113
|
+
end
|
114
|
+
unless @silent
|
115
|
+
puts "#{errors.size} errors. #{states.size} test cases executed. #{failures.size} failed"
|
116
|
+
unless failures.empty?
|
117
|
+
puts "Failures:"
|
118
|
+
puts specs.map{|spec| " #{spec.name} - #{spec.filename}" if failures.include?(spec.name)}.compact.join("\n")
|
119
|
+
end
|
120
|
+
end
|
121
|
+
return failures.size+errors.size
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
module Utilities
|
127
|
+
require "fileutils"
|
128
|
+
def self.write_file filename,content
|
129
|
+
FileUtils.mkdir_p(File.dirname(filename),:verbose=>false)
|
130
|
+
File.open(filename, 'wb') {|f| f.write(content) }
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
data/lib/rutema/core/runner.rb
CHANGED
@@ -1,84 +1,94 @@
|
|
1
|
-
# Copyright (c) 2007-2015 Vassilis Rizopoulos. All rights reserved.
|
2
|
-
|
3
|
-
require_relative "framework"
|
4
|
-
|
5
|
-
module Rutema
|
6
|
-
module Runners
|
7
|
-
class Default
|
8
|
-
include Rutema::Messaging
|
9
|
-
attr_reader :context
|
10
|
-
attr_accessor :setup,:teardown
|
11
|
-
def initialize context,queue
|
12
|
-
@setup=nil
|
13
|
-
@teardown=nil
|
14
|
-
@context=context || Hash.new
|
15
|
-
@queue = queue
|
16
|
-
@number_of_runs=0
|
17
|
-
end
|
18
|
-
|
19
|
-
def run spec
|
20
|
-
steps=[]
|
21
|
-
status=:success
|
22
|
-
state={'start_time'=>Time.now, "sequence_id"=>@number_of_runs,:test=>spec.name}
|
23
|
-
message(:test=>spec.name
|
24
|
-
if @setup
|
25
|
-
message(:test=>spec.name
|
26
|
-
executed_steps,status=run_scenario("_setup_",@setup.scenario,@context)
|
27
|
-
steps+=executed_steps
|
28
|
-
end
|
29
|
-
if status!=:error
|
30
|
-
message(:test=>spec.name
|
31
|
-
executed_steps,status=run_scenario(spec.name,spec.scenario,@context)
|
32
|
-
steps+=executed_steps
|
33
|
-
else
|
34
|
-
message(:test=>spec.name,'number'=>0,'status'=>:error,'out'=>"Setup failed",'err'=>"",'duration'=>0)
|
35
|
-
end
|
36
|
-
state['status']=status
|
37
|
-
if @teardown
|
38
|
-
message(:test=>spec.name
|
39
|
-
executed_steps,status=run_scenario("_teardown_",@teardown.scenario,@context)
|
40
|
-
end
|
41
|
-
message(:test=>spec.name
|
42
|
-
state["stop_time"]=Time.now
|
43
|
-
state['steps']=steps
|
44
|
-
@number_of_runs+=1
|
45
|
-
return state
|
46
|
-
end
|
47
|
-
|
48
|
-
private
|
49
|
-
def run_scenario name,scenario,meta
|
50
|
-
executed_steps=[]
|
51
|
-
status=:warning
|
52
|
-
begin
|
53
|
-
stps=scenario.steps
|
54
|
-
if stps.empty?
|
55
|
-
error(name,"Scenario #{name} contains no steps")
|
56
|
-
status=:error
|
57
|
-
else
|
58
|
-
stps.each do |s|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
error
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
1
|
+
# Copyright (c) 2007-2015 Vassilis Rizopoulos. All rights reserved.
|
2
|
+
|
3
|
+
require_relative "framework"
|
4
|
+
|
5
|
+
module Rutema
|
6
|
+
module Runners
|
7
|
+
class Default
|
8
|
+
include Rutema::Messaging
|
9
|
+
attr_reader :context
|
10
|
+
attr_accessor :setup,:teardown
|
11
|
+
def initialize context,queue
|
12
|
+
@setup=nil
|
13
|
+
@teardown=nil
|
14
|
+
@context=context || Hash.new
|
15
|
+
@queue = queue
|
16
|
+
@number_of_runs=0
|
17
|
+
end
|
18
|
+
|
19
|
+
def run spec
|
20
|
+
steps=[]
|
21
|
+
status=:success
|
22
|
+
state={'start_time'=>Time.now, "sequence_id"=>@number_of_runs,:test=>spec.name}
|
23
|
+
message(:test=>spec.name,:text=>'started')
|
24
|
+
if @setup
|
25
|
+
message(:test=>spec.name,:text=>'setup')
|
26
|
+
executed_steps,status=run_scenario("_setup_",@setup.scenario,@context)
|
27
|
+
steps+=executed_steps
|
28
|
+
end
|
29
|
+
if status!=:error
|
30
|
+
message(:test=>spec.name,:text=>'running')
|
31
|
+
executed_steps,status=run_scenario(spec.name,spec.scenario,@context)
|
32
|
+
steps+=executed_steps
|
33
|
+
else
|
34
|
+
message(:test=>spec.name,'number'=>0,'status'=>:error,'out'=>"Setup failed",'err'=>"",'duration'=>0)
|
35
|
+
end
|
36
|
+
state['status']=status
|
37
|
+
if @teardown
|
38
|
+
message(:test=>spec.name,:text=>'teardown')
|
39
|
+
executed_steps,status=run_scenario("_teardown_",@teardown.scenario,@context)
|
40
|
+
end
|
41
|
+
message(:test=>spec.name,:text=>'finished')
|
42
|
+
state["stop_time"]=Time.now
|
43
|
+
state['steps']=steps
|
44
|
+
@number_of_runs+=1
|
45
|
+
return state
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
def run_scenario name,scenario,meta
|
50
|
+
executed_steps=[]
|
51
|
+
status=:warning
|
52
|
+
begin
|
53
|
+
stps=scenario.steps
|
54
|
+
if stps.empty?
|
55
|
+
error(name,"Scenario #{name} contains no steps")
|
56
|
+
status=:error
|
57
|
+
else
|
58
|
+
stps.each do |s|
|
59
|
+
executed_steps<<run_step(s,meta)
|
60
|
+
message(:test=>name,:text=>s.to_s,'number'=>s.number,'status'=>s.status,'out'=>s.output,'err'=>s.error,'duration'=>s.exec_time)
|
61
|
+
status=s.status
|
62
|
+
break if :error==s.status
|
63
|
+
end
|
64
|
+
end
|
65
|
+
rescue
|
66
|
+
error(name,$!.message)
|
67
|
+
status=:error
|
68
|
+
end
|
69
|
+
return executed_steps,status
|
70
|
+
end
|
71
|
+
def run_step step,meta
|
72
|
+
if step.has_cmd? && step.cmd.respond_to?(:run)
|
73
|
+
step.cmd.run(meta)
|
74
|
+
else
|
75
|
+
message("No command associated with step '#{step.step_type}'. Step number is #{step.number}")
|
76
|
+
step.status=:warning
|
77
|
+
end
|
78
|
+
step.status=:success if step.ignore?
|
79
|
+
return step
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
class NoOp<Default
|
84
|
+
def run_step step,meta
|
85
|
+
unless step.has_cmd? && step.cmd.respond_to?(:run)
|
86
|
+
message("No command associated with step '#{step.step_type}'. Step number is #{step.number}")
|
87
|
+
step.status=:warning
|
88
|
+
end
|
89
|
+
step.status=:success if step.ignore?
|
90
|
+
return step
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
84
94
|
end
|
@@ -1,48 +1,48 @@
|
|
1
|
-
# Copyright (c) 2007-2015 Vassilis Rizopoulos. All rights reserved.
|
2
|
-
require 'highline'
|
3
|
-
module Rutema
|
4
|
-
#The Elements module provides the namespace for the various modules adding parser functionality
|
5
|
-
module Elements
|
6
|
-
#Minimal offers a minimal(chic) set of elements for use in specifications
|
7
|
-
#
|
8
|
-
#These are:
|
9
|
-
# echo
|
10
|
-
# command
|
11
|
-
# prompt
|
12
|
-
module Minimal
|
13
|
-
#echo prints a message on the screen:
|
14
|
-
# <echo text="A meaningful message"/>
|
15
|
-
# <echo>A meaningful message</echo>
|
16
|
-
def element_echo step
|
17
|
-
step.cmd=Patir::RubyCommand.new("echo"){|cmd| cmd.error="";cmd.output="#{step.text}";$stdout.puts(cmd.output) ;:success}
|
18
|
-
return step
|
19
|
-
end
|
20
|
-
#prompt asks the user a yes/no question. Answering yes means the step is succesful.
|
21
|
-
# <prompt text="Do you want fries with that?"/>
|
22
|
-
#
|
23
|
-
#A prompt element automatically makes a specification "attended"
|
24
|
-
def element_prompt step
|
25
|
-
step.attended=true
|
26
|
-
step.cmd=Patir::RubyCommand.new("prompt") do |cmd|
|
27
|
-
cmd.output=""
|
28
|
-
cmd.error=""
|
29
|
-
if HighLine.new.agree("#{step.text}")
|
30
|
-
step.output="y"
|
31
|
-
else
|
32
|
-
raise "n"
|
33
|
-
end#if
|
34
|
-
end#do rubycommand
|
35
|
-
return step
|
36
|
-
end
|
37
|
-
#command executes a shell command
|
38
|
-
# <command cmd="useful_command.exe with parameters", working_directory="some/directory"/>
|
39
|
-
def element_command step
|
40
|
-
raise ParserError,"missing required attribute cmd in #{step}" unless step.has_cmd?
|
41
|
-
wd=Dir.pwd
|
42
|
-
wd=step.working_directory if step.has_working_directory?
|
43
|
-
step.cmd=Patir::ShellCommand.new(:cmd=>step.cmd,:working_directory=>File.expand_path(wd))
|
44
|
-
return step
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
1
|
+
# Copyright (c) 2007-2015 Vassilis Rizopoulos. All rights reserved.
|
2
|
+
require 'highline'
|
3
|
+
module Rutema
|
4
|
+
#The Elements module provides the namespace for the various modules adding parser functionality
|
5
|
+
module Elements
|
6
|
+
#Minimal offers a minimal(chic) set of elements for use in specifications
|
7
|
+
#
|
8
|
+
#These are:
|
9
|
+
# echo
|
10
|
+
# command
|
11
|
+
# prompt
|
12
|
+
module Minimal
|
13
|
+
#echo prints a message on the screen:
|
14
|
+
# <echo text="A meaningful message"/>
|
15
|
+
# <echo>A meaningful message</echo>
|
16
|
+
def element_echo step
|
17
|
+
step.cmd=Patir::RubyCommand.new("echo"){|cmd| cmd.error="";cmd.output="#{step.text}";$stdout.puts(cmd.output) ;:success}
|
18
|
+
return step
|
19
|
+
end
|
20
|
+
#prompt asks the user a yes/no question. Answering yes means the step is succesful.
|
21
|
+
# <prompt text="Do you want fries with that?"/>
|
22
|
+
#
|
23
|
+
#A prompt element automatically makes a specification "attended"
|
24
|
+
def element_prompt step
|
25
|
+
step.attended=true
|
26
|
+
step.cmd=Patir::RubyCommand.new("prompt") do |cmd|
|
27
|
+
cmd.output=""
|
28
|
+
cmd.error=""
|
29
|
+
if HighLine.new.agree("#{step.text}")
|
30
|
+
step.output="y"
|
31
|
+
else
|
32
|
+
raise "n"
|
33
|
+
end#if
|
34
|
+
end#do rubycommand
|
35
|
+
return step
|
36
|
+
end
|
37
|
+
#command executes a shell command
|
38
|
+
# <command cmd="useful_command.exe with parameters", working_directory="some/directory"/>
|
39
|
+
def element_command step
|
40
|
+
raise ParserError,"missing required attribute cmd in #{step}" unless step.has_cmd?
|
41
|
+
wd=Dir.pwd
|
42
|
+
wd=step.working_directory if step.has_working_directory?
|
43
|
+
step.cmd=Patir::ShellCommand.new(:cmd=>step.cmd,:working_directory=>File.expand_path(wd))
|
44
|
+
return step
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
48
|
end
|