rutema 2.0.0 → 2.0.1
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 +5 -5
- data/History.txt +2 -0
- data/README.md +3 -3
- data/bin/rutema +4 -2
- data/lib/rutema/application.rb +22 -4
- data/lib/rutema/core/configuration.rb +277 -98
- data/lib/rutema/core/engine.rb +177 -33
- data/lib/rutema/core/framework.rb +115 -26
- data/lib/rutema/core/objectmodel.rb +284 -222
- data/lib/rutema/core/parser.rb +41 -10
- data/lib/rutema/core/reporter.rb +39 -16
- data/lib/rutema/core/runner.rb +103 -35
- data/lib/rutema/elements/minimal.rb +12 -9
- data/lib/rutema/parsers/xml.rb +63 -11
- data/lib/rutema/reporters/json.rb +4 -2
- data/lib/rutema/reporters/junit.rb +35 -12
- data/lib/rutema/version.rb +21 -6
- metadata +24 -20
data/lib/rutema/core/reporter.rb
CHANGED
|
@@ -1,19 +1,42 @@
|
|
|
1
|
-
# Copyright (c) 2007-
|
|
1
|
+
# Copyright (c) 2007-2021 Vassilis Rizopoulos. All rights reserved.
|
|
2
|
+
|
|
2
3
|
module Rutema
|
|
3
|
-
|
|
4
|
+
##
|
|
5
|
+
# Module for the definition of reporter classes
|
|
4
6
|
#
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
+
# rutema supports two kinds of reporters. Reporters derived from BlockReporter
|
|
8
|
+
# are supposed to receive data via the #report method at the end of a Rutema
|
|
9
|
+
# run. Reporters derived from EventReporter are intended to receive events
|
|
10
|
+
# continuously during a run via the #update method.
|
|
7
11
|
#
|
|
8
|
-
#Nothing
|
|
12
|
+
# Nothing permits implementing a reporter class which supports both
|
|
13
|
+
# behaviours.
|
|
9
14
|
module Reporters
|
|
15
|
+
##
|
|
16
|
+
# Base class for block reporters
|
|
17
|
+
#
|
|
18
|
+
# Block reporters are invoked at the end/after a test run. They offer means
|
|
19
|
+
# to e.g. print a summary after a test run or create a test report file for
|
|
20
|
+
# CI integration (see Reporters::JUnit as an example).
|
|
10
21
|
class BlockReporter
|
|
22
|
+
##
|
|
23
|
+
# Initialize a new instance from the given configuration
|
|
24
|
+
#
|
|
25
|
+
# * +configuration+ - the Configuration instance of the test run
|
|
26
|
+
# * +dispatcher+ - unused
|
|
11
27
|
def initialize configuration,dispatcher
|
|
12
28
|
@configuration=configuration
|
|
13
29
|
end
|
|
30
|
+
|
|
31
|
+
##
|
|
32
|
+
#
|
|
14
33
|
def report specifications,states,errors
|
|
15
34
|
end
|
|
16
35
|
end
|
|
36
|
+
|
|
37
|
+
##
|
|
38
|
+
# Event reporters receive and process information continually during a test
|
|
39
|
+
# run
|
|
17
40
|
class EventReporter
|
|
18
41
|
def initialize configuration,dispatcher
|
|
19
42
|
@configuration=configuration
|
|
@@ -23,16 +46,13 @@ module Rutema
|
|
|
23
46
|
def run!
|
|
24
47
|
@thread=Thread.new do
|
|
25
48
|
while true do
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
raise
|
|
33
|
-
end
|
|
49
|
+
data=@queue.pop
|
|
50
|
+
begin
|
|
51
|
+
update(data) if data
|
|
52
|
+
rescue
|
|
53
|
+
puts "#{self.class} failed with #{$!.message}"
|
|
54
|
+
raise
|
|
34
55
|
end
|
|
35
|
-
sleep 0.1
|
|
36
56
|
end
|
|
37
57
|
end
|
|
38
58
|
end
|
|
@@ -94,8 +114,10 @@ module Rutema
|
|
|
94
114
|
when RunnerMessage
|
|
95
115
|
if message.status == :error
|
|
96
116
|
puts "FATAL|#{message.to_s}"
|
|
117
|
+
elsif message.status == :warning
|
|
118
|
+
puts "WARNING|#{message.to_s}"
|
|
97
119
|
else
|
|
98
|
-
puts message.to_s if @mode=="verbose"
|
|
120
|
+
puts "#{message.to_s} #{message.status}." if @mode=="verbose"
|
|
99
121
|
end
|
|
100
122
|
when ErrorMessage
|
|
101
123
|
puts message.to_s
|
|
@@ -116,7 +138,8 @@ module Rutema
|
|
|
116
138
|
states.each{|k,v| failures<<v.test if v.status==:error}
|
|
117
139
|
|
|
118
140
|
unless @silent
|
|
119
|
-
|
|
141
|
+
count_tests_run = states.select { |name, state| !state.is_special }.count
|
|
142
|
+
puts "#{errors.size} errors. #{count_tests_run} test cases executed. #{failures.size} failed"
|
|
120
143
|
unless failures.empty?
|
|
121
144
|
puts "Failures:"
|
|
122
145
|
puts specs.map{|spec| " #{spec.name} - #{spec.filename}" if failures.include?(spec.name)}.compact.join("\n")
|
data/lib/rutema/core/runner.rb
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright (c) 2007-
|
|
1
|
+
# Copyright (c) 2007-2021 Vassilis Rizopoulos. All rights reserved.
|
|
2
2
|
|
|
3
3
|
require_relative "framework"
|
|
4
4
|
|
|
@@ -6,6 +6,7 @@ module Rutema
|
|
|
6
6
|
module Runners
|
|
7
7
|
class Default
|
|
8
8
|
include Rutema::Messaging
|
|
9
|
+
|
|
9
10
|
attr_reader :context
|
|
10
11
|
attr_accessor :setup,:teardown
|
|
11
12
|
def initialize context,queue
|
|
@@ -14,41 +15,67 @@ module Rutema
|
|
|
14
15
|
@context=context || Hash.new
|
|
15
16
|
@queue = queue
|
|
16
17
|
@number_of_runs=0
|
|
18
|
+
@cleanup_blocks = []
|
|
17
19
|
end
|
|
18
20
|
|
|
19
|
-
def run
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
message(:test=>spec.name,:text=>'
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
21
|
+
def run(spec, is_special = false)
|
|
22
|
+
begin
|
|
23
|
+
@context["spec_name"]=spec.name
|
|
24
|
+
steps=[]
|
|
25
|
+
status=:success
|
|
26
|
+
state={'start_time'=>Time.now, "sequence_id"=>@number_of_runs,:test=>spec.name}
|
|
27
|
+
message(:test=>spec.name,:text=>'started')
|
|
28
|
+
if @setup
|
|
29
|
+
message(:test=>spec.name,:text=>'setup')
|
|
30
|
+
executed_steps,setup_status = run_scenario("_setup_", @setup.scenario, @context, true)
|
|
31
|
+
status=setup_status unless STATUS_CODES.find_index(setup_status) < STATUS_CODES.find_index(status)
|
|
32
|
+
steps+=executed_steps
|
|
33
|
+
end
|
|
34
|
+
if status!=:error
|
|
35
|
+
message(:test=>spec.name,:text=>'running')
|
|
36
|
+
executed_steps,testspec_status = run_scenario(spec.name, spec.scenario, @context, is_special)
|
|
37
|
+
status=testspec_status unless STATUS_CODES.find_index(testspec_status) < STATUS_CODES.find_index(status)
|
|
38
|
+
steps+=executed_steps
|
|
39
|
+
else
|
|
40
|
+
message(:test=>spec.name,'number'=>0,'status'=>:error,'out'=>"Setup failed",'err'=>"",'duration'=>0)
|
|
41
|
+
end
|
|
42
|
+
@context['rutema_status']=status
|
|
43
|
+
if @teardown
|
|
44
|
+
message(:test=>spec.name,:text=>'teardown')
|
|
45
|
+
executed_steps,teardown_status = run_scenario("_teardown_", @teardown.scenario, @context, true)
|
|
46
|
+
status=teardown_status unless STATUS_CODES.find_index(teardown_status) < STATUS_CODES.find_index(status)
|
|
47
|
+
end
|
|
48
|
+
@context['rutema_status']=status
|
|
49
|
+
message(:test=>spec.name,:text=>'finished')
|
|
50
|
+
state['status']=status
|
|
51
|
+
state["stop_time"]=Time.now
|
|
52
|
+
state['steps']=steps
|
|
53
|
+
@number_of_runs+=1
|
|
54
|
+
return state
|
|
55
|
+
ensure
|
|
56
|
+
begin
|
|
57
|
+
cleanup_exception = nil
|
|
58
|
+
@cleanup_blocks.each do |cleanup_block|
|
|
59
|
+
# Try all blocks
|
|
60
|
+
begin
|
|
61
|
+
cleanup_block.run(@context) if cleanup_block.respond_to?(:run)
|
|
62
|
+
rescue Exception => e
|
|
63
|
+
# Ignore errors, ensure all cleanup steps are attempted
|
|
64
|
+
cleanup_exception = e
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
raise cleanup_exception if !cleanup_exception.nil?
|
|
68
|
+
ensure
|
|
69
|
+
@cleanup_blocks = []
|
|
70
|
+
end
|
|
40
71
|
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
72
|
end
|
|
47
73
|
|
|
48
74
|
private
|
|
49
|
-
|
|
75
|
+
|
|
76
|
+
def run_scenario(name, scenario, meta, is_special)
|
|
50
77
|
executed_steps=[]
|
|
51
|
-
status=:
|
|
78
|
+
status=:skipped
|
|
52
79
|
begin
|
|
53
80
|
stps=scenario.steps
|
|
54
81
|
if stps.empty?
|
|
@@ -56,18 +83,45 @@ module Rutema
|
|
|
56
83
|
status=:error
|
|
57
84
|
else
|
|
58
85
|
stps.each do |s|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
86
|
+
if status == :error && s.skip_on_error?
|
|
87
|
+
message(:test=>name,:text=>s.to_s,'number'=>s.number,'status'=>:skipped,'is_special'=>is_special)
|
|
88
|
+
else
|
|
89
|
+
message(
|
|
90
|
+
:test => name, :text => s.to_s, 'number' => s.number,
|
|
91
|
+
'status' => :started, 'is_special' => is_special
|
|
92
|
+
)
|
|
93
|
+
sleep 0.05
|
|
94
|
+
begin
|
|
95
|
+
cache_cleanup(s)
|
|
96
|
+
executed_steps << run_step(s, meta)
|
|
97
|
+
rescue Exception => e
|
|
98
|
+
throw e unless s.continue?
|
|
99
|
+
s.status = :error
|
|
100
|
+
end
|
|
101
|
+
message(
|
|
102
|
+
:test => name, :text => s.to_s, 'number' => s.number,
|
|
103
|
+
'status' => s.status, 'out' => s.output, 'err' => s.error,
|
|
104
|
+
'backtrace' => s.backtrace, 'duration' => s.exec_time,
|
|
105
|
+
'is_special' => is_special
|
|
106
|
+
)
|
|
107
|
+
status=s.status unless STATUS_CODES.find_index(s.status) < STATUS_CODES.find_index(status)
|
|
108
|
+
break if :error == s.status and !s.continue?
|
|
109
|
+
end
|
|
63
110
|
end
|
|
64
111
|
end
|
|
65
|
-
rescue
|
|
112
|
+
rescue
|
|
66
113
|
error(name,$!.message)
|
|
67
114
|
status=:error
|
|
68
115
|
end
|
|
69
116
|
return executed_steps,status
|
|
70
117
|
end
|
|
118
|
+
|
|
119
|
+
def cache_cleanup(step)
|
|
120
|
+
if step.has_cleanup? && step.cleanup.respond_to?(:run)
|
|
121
|
+
@cleanup_blocks << step.cleanup
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
71
125
|
def run_step step,meta
|
|
72
126
|
if step.has_cmd? && step.cmd.respond_to?(:run)
|
|
73
127
|
step.cmd.run(meta)
|
|
@@ -80,7 +134,21 @@ module Rutema
|
|
|
80
134
|
end
|
|
81
135
|
end
|
|
82
136
|
|
|
137
|
+
##
|
|
138
|
+
# Fake runner which does not run the passed steps but just sets their
|
|
139
|
+
# execution status to +:success+
|
|
140
|
+
#
|
|
141
|
+
# Steps that do not respond to +:run+ have their status set to +:warning+.
|
|
142
|
+
#
|
|
143
|
+
# Returns the step after "executing" it successfully
|
|
83
144
|
class NoOp<Default
|
|
145
|
+
##
|
|
146
|
+
# Simulate running the step by setting its status to +:success+
|
|
147
|
+
#
|
|
148
|
+
# If the step does not respond to +:run+ then +:warning+ is set as its
|
|
149
|
+
# status.
|
|
150
|
+
#
|
|
151
|
+
# * +step+ -
|
|
84
152
|
def run_step step,meta
|
|
85
153
|
unless step.has_cmd? && step.cmd.respond_to?(:run)
|
|
86
154
|
message("No command associated with step '#{step.step_type}'. Step number is #{step.number}")
|
|
@@ -91,4 +159,4 @@ module Rutema
|
|
|
91
159
|
end
|
|
92
160
|
end
|
|
93
161
|
end
|
|
94
|
-
end
|
|
162
|
+
end
|
|
@@ -1,14 +1,15 @@
|
|
|
1
|
-
# Copyright (c) 2007-
|
|
1
|
+
# Copyright (c) 2007-2021 Vassilis Rizopoulos. All rights reserved.
|
|
2
|
+
|
|
2
3
|
require 'highline'
|
|
4
|
+
|
|
3
5
|
module Rutema
|
|
4
|
-
|
|
6
|
+
##
|
|
7
|
+
# Module providing a namespace for modules which are used to add to a parsers
|
|
8
|
+
# functionality which can then be utilized in test specifications
|
|
5
9
|
module Elements
|
|
6
|
-
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
# echo
|
|
10
|
-
# command
|
|
11
|
-
# prompt
|
|
10
|
+
##
|
|
11
|
+
# Module offering an examplary minimal set of elements for use as steps in
|
|
12
|
+
# test specifications
|
|
12
13
|
module Minimal
|
|
13
14
|
#echo prints a message on the screen:
|
|
14
15
|
# <echo text="A meaningful message"/>
|
|
@@ -17,6 +18,7 @@ module Rutema
|
|
|
17
18
|
step.cmd=Patir::RubyCommand.new("echo"){|cmd| cmd.error="";cmd.output="#{step.text}";$stdout.puts(cmd.output) ;:success}
|
|
18
19
|
return step
|
|
19
20
|
end
|
|
21
|
+
|
|
20
22
|
#prompt asks the user a yes/no question. Answering yes means the step is succesful.
|
|
21
23
|
# <prompt text="Do you want fries with that?"/>
|
|
22
24
|
#
|
|
@@ -34,6 +36,7 @@ module Rutema
|
|
|
34
36
|
end#do rubycommand
|
|
35
37
|
return step
|
|
36
38
|
end
|
|
39
|
+
|
|
37
40
|
#command executes a shell command
|
|
38
41
|
# <command cmd="useful_command.exe with parameters", working_directory="some/directory"/>
|
|
39
42
|
def element_command step
|
|
@@ -45,4 +48,4 @@ module Rutema
|
|
|
45
48
|
end
|
|
46
49
|
end
|
|
47
50
|
end
|
|
48
|
-
end
|
|
51
|
+
end
|
data/lib/rutema/parsers/xml.rb
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
# Copyright (c) 2007-
|
|
1
|
+
# Copyright (c) 2007-2021 Vassilis Rizopoulos. All rights reserved.
|
|
2
|
+
|
|
2
3
|
require 'rexml/document'
|
|
3
4
|
require 'patir/command'
|
|
4
5
|
require_relative '../core/parser'
|
|
@@ -7,14 +8,17 @@ require_relative '../elements/minimal'
|
|
|
7
8
|
|
|
8
9
|
module Rutema
|
|
9
10
|
module Parsers
|
|
10
|
-
|
|
11
|
+
##
|
|
12
|
+
# Basic and easily extendable parser for test specifications in XML format
|
|
11
13
|
#
|
|
12
|
-
#
|
|
13
|
-
#a method
|
|
14
|
+
# Actual XML specification parsers should be derived from this class and
|
|
15
|
+
# define for each specification element +foo+ to be parsed a method
|
|
16
|
+
# +element_foo+.
|
|
14
17
|
#
|
|
15
|
-
#The method will receive a Rutema::Step instance as a parameter which it should return
|
|
18
|
+
# The method will receive a Rutema::Step instance as a parameter which it should return
|
|
16
19
|
class XML<SpecificationParser
|
|
17
20
|
include Rutema::Elements::Minimal
|
|
21
|
+
|
|
18
22
|
#:nodoc:
|
|
19
23
|
ELEM_SPEC="specification"
|
|
20
24
|
#:nodoc:
|
|
@@ -23,11 +27,15 @@ module Rutema
|
|
|
23
27
|
ELEM_TITLE="specification/title"
|
|
24
28
|
#:nodoc:
|
|
25
29
|
ELEM_SCENARIO="specification/scenario"
|
|
26
|
-
|
|
30
|
+
|
|
31
|
+
##
|
|
32
|
+
# Pass the given test specification and return a corresponding
|
|
33
|
+
# Specification instance
|
|
27
34
|
#
|
|
28
|
-
#
|
|
35
|
+
# The passed argument can either be the path to a test specification file
|
|
36
|
+
# or the test specification itself.
|
|
29
37
|
#
|
|
30
|
-
#
|
|
38
|
+
# This will raise ParserError if an error occurs during parsing.
|
|
31
39
|
def parse_specification param
|
|
32
40
|
@parsed||=[]
|
|
33
41
|
begin
|
|
@@ -47,8 +55,17 @@ module Rutema
|
|
|
47
55
|
raise Rutema::ParserError,$!.message
|
|
48
56
|
end
|
|
49
57
|
end
|
|
58
|
+
|
|
50
59
|
private
|
|
51
|
-
|
|
60
|
+
|
|
61
|
+
##
|
|
62
|
+
# Parse the XML specification of a testcase and create a corresponding
|
|
63
|
+
# Rutema::Specification instance
|
|
64
|
+
#
|
|
65
|
+
# * +xmltext+ - the actual test specification text which must be an XML
|
|
66
|
+
# document
|
|
67
|
+
# * +filename+ - the filename of the test specification file or the
|
|
68
|
+
# current working directory of the test execution (this
|
|
52
69
|
def parse_case xmltxt,filename
|
|
53
70
|
spec=Rutema::Specification.new({})
|
|
54
71
|
xmldoc=REXML::Document.new( xmltxt )
|
|
@@ -71,12 +88,21 @@ module Rutema
|
|
|
71
88
|
spec.filename=filename
|
|
72
89
|
return spec
|
|
73
90
|
end
|
|
74
|
-
|
|
91
|
+
|
|
92
|
+
##
|
|
93
|
+
# Conduct a simple validation of the XML document by checking if it
|
|
94
|
+
# contains all necessary elements
|
|
95
|
+
#
|
|
96
|
+
# ParserError is being raised if any of the necessary elements is missing.
|
|
97
|
+
#
|
|
98
|
+
# * +xmldoc+ - the text of the XML document to be checked for the
|
|
99
|
+
# necessary elements
|
|
75
100
|
def validate_case xmldoc
|
|
76
101
|
raise Rutema::ParserError,"missing #{ELEM_SPEC} element in #{xmldoc}" unless xmldoc.elements[ELEM_SPEC]
|
|
77
102
|
raise Rutema::ParserError,"missing #{ELEM_DESC} element in #{xmldoc}" unless xmldoc.elements[ELEM_DESC]
|
|
78
103
|
raise Rutema::ParserError,"missing #{ELEM_TITLE} element in #{xmldoc}" unless xmldoc.elements[ELEM_TITLE]
|
|
79
104
|
end
|
|
105
|
+
|
|
80
106
|
#Parses the 'scenario' XML element and returns the Rutema::Scenario instance
|
|
81
107
|
def parse_scenario xmltxt
|
|
82
108
|
scenario=Rutema::Scenario.new([])
|
|
@@ -103,12 +129,14 @@ module Rutema
|
|
|
103
129
|
end
|
|
104
130
|
return scenario
|
|
105
131
|
end
|
|
132
|
+
|
|
106
133
|
#Parses xml and returns the Rutema::Step instance
|
|
107
134
|
def parse_step xmltxt
|
|
108
135
|
xmldoc=REXML::Document.new( xmltxt )
|
|
109
136
|
#any step element
|
|
110
137
|
step=Rutema::Step.new()
|
|
111
138
|
step.ignore=false
|
|
139
|
+
step.continue=false
|
|
112
140
|
xmldoc.root.attributes.each do |attr,value|
|
|
113
141
|
add_attribute(step,attr,value)
|
|
114
142
|
end
|
|
@@ -116,17 +144,38 @@ module Rutema
|
|
|
116
144
|
step.step_type=xmldoc.root.name
|
|
117
145
|
return step
|
|
118
146
|
end
|
|
147
|
+
|
|
148
|
+
##
|
|
149
|
+
# Add an attribute of a given name with the given value to a specification
|
|
150
|
+
# element
|
|
151
|
+
#
|
|
152
|
+
# * +element+ - the specification element the attribute shall be added to
|
|
153
|
+
# * +attr+ - the name of the attribute which shall either be created or
|
|
154
|
+
# whose current value will be overridden
|
|
155
|
+
# * +value+ - the value which shall be set for the attribute
|
|
119
156
|
def add_attribute element,attr,value
|
|
157
|
+
# If the string is a textual representation of a boolean value ...
|
|
120
158
|
if boolean?(value)
|
|
159
|
+
# ... convert it to a boolean value
|
|
121
160
|
element.attribute(attr,eval(value))
|
|
122
161
|
else
|
|
123
162
|
element.attribute(attr,value)
|
|
124
163
|
end
|
|
125
164
|
end
|
|
165
|
+
|
|
166
|
+
##
|
|
167
|
+
# Check if attribute_value is a string representing a boolean value
|
|
168
|
+
#
|
|
169
|
+
# This returns +true+ if the string is "true" or "false" or +false+
|
|
170
|
+
# otherwise.
|
|
171
|
+
#
|
|
172
|
+
# * +attribute_value+ - the entity which shall be checked if it's a string
|
|
173
|
+
# representing a boolean value
|
|
126
174
|
def boolean? attribute_value
|
|
127
175
|
return true if attribute_value=="true" || attribute_value=="false"
|
|
128
176
|
return false
|
|
129
177
|
end
|
|
178
|
+
|
|
130
179
|
#handles <include_scenario> elements, adding the steps to the current scenario
|
|
131
180
|
def include_scenario step
|
|
132
181
|
raise Rutema::ParserError,"missing required attribute file in #{step}" unless step.has_file?
|
|
@@ -135,6 +184,9 @@ module Rutema
|
|
|
135
184
|
include_content=File.read(step.file)
|
|
136
185
|
return parse_scenario(include_content)
|
|
137
186
|
end
|
|
187
|
+
|
|
188
|
+
##
|
|
189
|
+
#
|
|
138
190
|
def extension_handling spec
|
|
139
191
|
#change into the directory the spec is in to handle relative paths correctly
|
|
140
192
|
Dir.chdir(File.dirname(File.expand_path(spec.filename))) do |path|
|
|
@@ -144,7 +196,7 @@ module Rutema
|
|
|
144
196
|
begin
|
|
145
197
|
self.send(:"element_#{step.step_type}",step)
|
|
146
198
|
rescue
|
|
147
|
-
raise ParserError, $!.message
|
|
199
|
+
raise ParserError, ($!.message + "\n" + $@.join("\n"))
|
|
148
200
|
end#begin
|
|
149
201
|
elsif @configuration.parser["strict_mode"]
|
|
150
202
|
raise ParserError,"No command element associated with #{step.step_type}. Missing element_#{step.step_type}"
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
# Copyright (c) 2007-
|
|
1
|
+
# Copyright (c) 2007-2021 Vassilis Rizopoulos. All rights reserved.
|
|
2
|
+
|
|
2
3
|
require 'json'
|
|
3
4
|
require_relative "../core/reporter"
|
|
4
5
|
|
|
@@ -12,11 +13,12 @@ module Rutema
|
|
|
12
13
|
class JSON<Rutema::Reporters::BlockReporter
|
|
13
14
|
#Default report filename
|
|
14
15
|
DEFAULT_FILENAME="rutema.results.json"
|
|
15
|
-
|
|
16
|
+
|
|
16
17
|
def initialize configuration,dispatcher
|
|
17
18
|
super(configuration,dispatcher)
|
|
18
19
|
@filename=configuration.reporters.fetch(self.class,{}).fetch("filename",DEFAULT_FILENAME)
|
|
19
20
|
end
|
|
21
|
+
|
|
20
22
|
#We get all the data from a test run in here.
|
|
21
23
|
def report specs,states,errors
|
|
22
24
|
run_entry={}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
# Copyright (c) 2015 Vassilis Rizopoulos. All rights reserved.
|
|
1
|
+
# Copyright (c) 2015-2021 Vassilis Rizopoulos. All rights reserved.
|
|
2
|
+
|
|
2
3
|
require 'rexml/document'
|
|
3
4
|
require_relative "../core/reporter"
|
|
4
5
|
|
|
@@ -18,16 +19,18 @@ module Rutema
|
|
|
18
19
|
# cfg.reporter={:class=>Rutema::Reporters::JUnit,"filename"=>"rutema.junit.xml"}
|
|
19
20
|
class JUnit<BlockReporter
|
|
20
21
|
DEFAULT_FILENAME="rutema.results.junit.xml"
|
|
21
|
-
|
|
22
|
+
|
|
22
23
|
def initialize configuration,dispatcher
|
|
23
24
|
super(configuration,dispatcher)
|
|
24
25
|
@filename=configuration.reporters.fetch(self.class,{}).fetch("filename",DEFAULT_FILENAME)
|
|
25
26
|
end
|
|
27
|
+
|
|
26
28
|
#We get all the data from a test run in here.
|
|
27
29
|
def report specs,states,errors
|
|
28
30
|
cnt=process_data(specs,states,errors)
|
|
29
31
|
Rutema::Utilities.write_file(@filename,cnt)
|
|
30
32
|
end
|
|
33
|
+
|
|
31
34
|
def process_data specs,states,errors
|
|
32
35
|
tests=[]
|
|
33
36
|
number_of_failed=0
|
|
@@ -49,7 +52,9 @@ module Rutema
|
|
|
49
52
|
}
|
|
50
53
|
return junit_content(tests,attributes,errors)
|
|
51
54
|
end
|
|
55
|
+
|
|
52
56
|
private
|
|
57
|
+
|
|
53
58
|
def test_case name,state
|
|
54
59
|
#<testcase name="" time=""> => the results from executing a test method
|
|
55
60
|
# <system-out> => data written to System.out during the test run
|
|
@@ -61,19 +66,35 @@ module Rutema
|
|
|
61
66
|
element_test=REXML::Element.new("testcase")
|
|
62
67
|
element_test.add_attributes("name"=>name,"time"=>state.duration,"classname"=>@configuration.context[:config_name])
|
|
63
68
|
if state.status!=:success
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
69
|
+
failed_steps = state.steps.select {|step| !step.status.nil? && STATUS_CODES.find_index(:success) < STATUS_CODES.find_index(step.status)}
|
|
70
|
+
if !failed_steps.empty?
|
|
71
|
+
failed_steps.each do |step|
|
|
72
|
+
fail=REXML::Element.new("failure")
|
|
73
|
+
fail.add_attribute("message","Step: #{step.text} reported non-success status: #{step.status}.")
|
|
74
|
+
fail.add_text "Step #{step.number} failed."
|
|
75
|
+
element_test.add_element(fail)
|
|
76
|
+
out=REXML::Element.new("system-out")
|
|
77
|
+
out.add_text step.out
|
|
78
|
+
element_test.add_element(out)
|
|
79
|
+
err=REXML::Element.new("system-err")
|
|
80
|
+
err.add_text step.err
|
|
81
|
+
element_test.add_element(err)
|
|
82
|
+
end
|
|
83
|
+
else
|
|
84
|
+
fail.add_attribute("message","Case reported non-success status: #{state.status} without a matching step state.")
|
|
85
|
+
fail.add_text "Case with #{state.steps.last.number} steps failed with no matching failed step."
|
|
86
|
+
element_test.add_element(fail)
|
|
87
|
+
out=REXML::Element.new("system-out")
|
|
88
|
+
out.add_text state.steps.last.out
|
|
89
|
+
element_test.add_element(out)
|
|
90
|
+
err=REXML::Element.new("system-err")
|
|
91
|
+
err.add_text state.steps.last.err
|
|
92
|
+
element_test.add_element(err)
|
|
93
|
+
end
|
|
74
94
|
end
|
|
75
95
|
return element_test
|
|
76
96
|
end
|
|
97
|
+
|
|
77
98
|
def crash name,message
|
|
78
99
|
failed=REXML::Element.new("testcase")
|
|
79
100
|
failed.add_attributes("name"=>name,"classname"=>@configuration.context[:config_name],"time"=>0)
|
|
@@ -83,6 +104,7 @@ module Rutema
|
|
|
83
104
|
failed.add_element(msg)
|
|
84
105
|
return failed
|
|
85
106
|
end
|
|
107
|
+
|
|
86
108
|
def junit_content tests,attributes,errors
|
|
87
109
|
element_suite=REXML::Element.new("testsuite")
|
|
88
110
|
element_suite.add_attributes(attributes)
|
|
@@ -90,6 +112,7 @@ module Rutema
|
|
|
90
112
|
tests.each{|t| element_suite.add_element(t)}
|
|
91
113
|
return document(element_suite).to_s
|
|
92
114
|
end
|
|
115
|
+
|
|
93
116
|
def document suite
|
|
94
117
|
xmldoc=REXML::Document.new
|
|
95
118
|
xmldoc<<REXML::XMLDecl.new
|
data/lib/rutema/version.rb
CHANGED
|
@@ -1,9 +1,24 @@
|
|
|
1
|
+
# Copyright (c) 2021 Vassilis Rizopoulos. All rights reserved.
|
|
2
|
+
|
|
3
|
+
# frozen_string_literal: true
|
|
4
|
+
|
|
5
|
+
##
|
|
6
|
+
# The top-level module of rutema encompassing all its functionality.
|
|
1
7
|
module Rutema
|
|
2
|
-
|
|
8
|
+
##
|
|
9
|
+
# Version information of the rutema gem
|
|
3
10
|
module Version
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
11
|
+
##
|
|
12
|
+
# The major version of the rutema gem
|
|
13
|
+
MAJOR = 2
|
|
14
|
+
##
|
|
15
|
+
# The minor version of the rutema gem
|
|
16
|
+
MINOR = 0
|
|
17
|
+
##
|
|
18
|
+
# The tiny version of the rutema gem
|
|
19
|
+
TINY = 1
|
|
20
|
+
##
|
|
21
|
+
# The version information of the rutema gem as a string
|
|
22
|
+
STRING = [MAJOR, MINOR, TINY].join(".")
|
|
8
23
|
end
|
|
9
|
-
end
|
|
24
|
+
end
|