rutema 1.1.3 → 1.2.0
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.
- data/.gemtest +0 -0
- data/History.txt +5 -0
- data/Manifest.txt +28 -23
- data/README.md +91 -0
- data/README.txt +2 -10
- data/Rakefile +6 -9
- data/bin/rutema +9 -0
- data/bin/rutemax +2 -0
- data/lib/rutema/configuration.rb +7 -8
- data/lib/rutema/elements/minimal.rb +45 -0
- data/lib/rutema/gems.rb +3 -3
- data/lib/rutema/models/activerecord.rb +164 -0
- data/lib/rutema/models/base.rb +5 -0
- data/lib/rutema/{specification.rb → objectmodel.rb} +4 -10
- data/lib/rutema/parsers/base.rb +29 -0
- data/lib/rutema/parsers/xml.rb +186 -0
- data/lib/rutema/reporters/activerecord.rb +9 -18
- data/lib/rutema/{reporter.rb → reporters/base.rb} +3 -7
- data/lib/rutema/reporters/email.rb +2 -5
- data/lib/rutema/reporters/text.rb +2 -2
- data/lib/rutema/reporters/yaml.rb +1 -2
- data/lib/rutema/runners/default.rb +157 -0
- data/lib/rutema/runners/step.rb +23 -0
- data/lib/rutema/system.rb +15 -420
- data/test/distro_test/config/full.rutema +2 -0
- data/test/distro_test/specs/check.spec +8 -0
- data/test/distro_test/specs/duplicate_name.spec +8 -0
- data/test/distro_test/specs/fail.spec +9 -0
- data/test/distro_test/specs/setup.spec +8 -0
- data/test/distro_test/specs/teardown.spec +8 -0
- data/test/rutema.rutema +1 -1
- data/test/rutema.spec +5 -5
- data/test/test_activerecord.rb +0 -0
- data/test/test_configuration.rb +7 -9
- data/test/test_couchdb.rb +14 -0
- data/test/{test_specification.rb → test_objectmodel.rb} +8 -11
- data/test/test_parsers.rb +136 -0
- data/test/test_rake.rb +2 -3
- data/test/{test_reporter.rb → test_reporters.rb} +0 -0
- data/test/test_runners.rb +72 -0
- data/test/test_system.rb +3 -166
- metadata +57 -62
- data/bin/rutema_upgrader +0 -81
- data/distro_test.sh +0 -6
- data/lib/rutema/model.rb +0 -231
- data/lib/rutema/reporters/couchdb.rb +0 -62
- data/lib/rutema/reporters/standard_reporters.rb +0 -7
- data/selftest.sh +0 -2
- data/test/data/sample09.db +0 -0
- data/test/migration.spec +0 -9
- data/test/test_model.rb +0 -62
@@ -1,6 +1,5 @@
|
|
1
1
|
# Copyright (c) 2007-2010 Vassilis Rizopoulos. All rights reserved.
|
2
2
|
$:.unshift File.join(File.dirname(__FILE__),"..")
|
3
|
-
require 'rutema/reporters/standard_reporters'
|
4
3
|
require 'patir/command'
|
5
4
|
|
6
5
|
module Rutema
|
@@ -37,8 +36,6 @@ module Rutema
|
|
37
36
|
end
|
38
37
|
end
|
39
38
|
end
|
40
|
-
|
41
|
-
|
42
39
|
#A TestSpecification encompasses all elements required to run a test, the builds used, the scenario to run,
|
43
40
|
#together with a textual description and information that aids in tracing the test back to the requirements.
|
44
41
|
class TestSpecification
|
@@ -74,12 +71,10 @@ module Rutema
|
|
74
71
|
@scenario=TestScenario.new(@attributes[:version])
|
75
72
|
@requirements||=Array.new
|
76
73
|
end
|
77
|
-
|
78
|
-
def to_s
|
74
|
+
def to_s#:nodoc:
|
79
75
|
return "#{@attributes[:name]} - #{@attributes[:title]}"
|
80
76
|
end
|
81
77
|
end
|
82
|
-
|
83
78
|
#A TestScenario is a sequence of TestStep instances.
|
84
79
|
#
|
85
80
|
#TestStep instances are run in the definition sequence and the scenario
|
@@ -121,7 +116,6 @@ module Rutema
|
|
121
116
|
end
|
122
117
|
end
|
123
118
|
end
|
124
|
-
|
125
119
|
#Represents a step in a TestScenario.
|
126
120
|
#
|
127
121
|
#Each TestStep can have text and a command associated with it.
|
@@ -210,7 +204,7 @@ module Rutema
|
|
210
204
|
param=" - #{self.cmd.to_s}" if self.has_cmd?
|
211
205
|
return "#{@attributes[:step_type]}#{param}"
|
212
206
|
end
|
213
|
-
def to_s
|
207
|
+
def to_s#:nodoc:
|
214
208
|
param=""
|
215
209
|
param=" - #{self.cmd.to_s}" if self.has_cmd?
|
216
210
|
msg="#{self.number} - #{self.step_type}#{param} - #{self.status}"
|
@@ -222,13 +216,13 @@ module Rutema
|
|
222
216
|
end
|
223
217
|
|
224
218
|
class Patir::ShellCommand
|
225
|
-
def to_s
|
219
|
+
def to_s#:nodoc:
|
226
220
|
return @command
|
227
221
|
end
|
228
222
|
end
|
229
223
|
|
230
224
|
class Patir::RubyCommand
|
231
|
-
def to_s
|
225
|
+
def to_s#:nodoc:
|
232
226
|
return @name
|
233
227
|
end
|
234
228
|
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# Copyright (c) 2007-2011 Vassilis Rizopoulos. All rights reserved.
|
2
|
+
$:.unshift File.join(File.dirname(__FILE__),'..','..')
|
3
|
+
|
4
|
+
module Rutema
|
5
|
+
#Is raised when an error is found in a specification
|
6
|
+
class ParserError<RuntimeError
|
7
|
+
end
|
8
|
+
#Base class that bombs out when used.
|
9
|
+
#
|
10
|
+
#Initialze expects a hash and as a base implementation assigns :logger as the internal logger.
|
11
|
+
#
|
12
|
+
#By default the internal logger will log to the console if no logger is provided.
|
13
|
+
class SpecificationParser
|
14
|
+
attr_reader :configuration
|
15
|
+
def initialize params
|
16
|
+
@configuration=params
|
17
|
+
@logger.warn("No system configuration provided to the parser") unless @configuration
|
18
|
+
@logger=@configuration[:logger]
|
19
|
+
unless @logger
|
20
|
+
@logger=Patir.setup_logger
|
21
|
+
@configuration[:logger]=@logger
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def parse_specification param
|
26
|
+
raise ParserError,"not implemented. You should derive a parser implementation from SpecificationParser!"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,186 @@
|
|
1
|
+
# Copyright (c) 2007-2011 Vassilis Rizopoulos. All rights reserved.
|
2
|
+
$:.unshift File.join(File.dirname(__FILE__),'..','..')
|
3
|
+
require 'rexml/document'
|
4
|
+
require 'patir/command'
|
5
|
+
require 'rutema/objectmodel'
|
6
|
+
require 'rutema/parsers/base'
|
7
|
+
require 'rutema/elements/minimal'
|
8
|
+
|
9
|
+
module Rutema
|
10
|
+
#BaseXMLParser encapsulates all the XML parsing code
|
11
|
+
class BaseXMLParser<SpecificationParser
|
12
|
+
ELEM_SPEC="specification"
|
13
|
+
ELEM_DESC="specification/description"
|
14
|
+
ELEM_TITLE="specification/title"
|
15
|
+
ELEM_SCENARIO="specification/scenario"
|
16
|
+
ELEM_REQ="requirement"
|
17
|
+
#Parses __param__ and returns the Rutema::TestSpecification instance
|
18
|
+
#
|
19
|
+
#param can be the filename of the specification or the contents of that file.
|
20
|
+
#
|
21
|
+
#Will throw ParserError if something goes wrong
|
22
|
+
def parse_specification param
|
23
|
+
@logger.debug("Loading #{param}")
|
24
|
+
begin
|
25
|
+
if File.exists?(param)
|
26
|
+
#read the file
|
27
|
+
txt=File.read(param)
|
28
|
+
filename=File.expand_path(param)
|
29
|
+
else
|
30
|
+
filename=Dir.pwd
|
31
|
+
#try to parse the parameter
|
32
|
+
txt=param
|
33
|
+
end
|
34
|
+
spec=parse_case(txt,filename)
|
35
|
+
raise "Missing required attribute 'name' in specification element" unless spec.has_name? && !spec.name.empty?
|
36
|
+
return spec
|
37
|
+
rescue
|
38
|
+
@logger.debug($!)
|
39
|
+
raise ParserError,"Error loading #{param}: #{$!.message}"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
#Parses the XML specification of a testcase and creates the corresponding TestSpecification instance
|
45
|
+
def parse_case xmltxt,filename
|
46
|
+
#the testspec to return
|
47
|
+
spec=TestSpecification.new
|
48
|
+
#read the test spec
|
49
|
+
xmldoc=REXML::Document.new( xmltxt )
|
50
|
+
#validate it
|
51
|
+
validate_case(xmldoc)
|
52
|
+
#parse it
|
53
|
+
el=xmldoc.elements[ELEM_SPEC]
|
54
|
+
xmldoc.root.attributes.each do |attr,value|
|
55
|
+
add_attribute(spec,attr,value)
|
56
|
+
end
|
57
|
+
#get the title
|
58
|
+
spec.title=xmldoc.elements[ELEM_TITLE].text
|
59
|
+
spec.title||=""
|
60
|
+
spec.title.strip!
|
61
|
+
#get the description
|
62
|
+
#strip line feeds, cariage returns and remove all tabs
|
63
|
+
spec.description=xmldoc.elements[ELEM_DESC].text
|
64
|
+
spec.description||=""
|
65
|
+
begin
|
66
|
+
spec.description.strip!
|
67
|
+
spec.description.gsub!(/\t/,'')
|
68
|
+
end unless spec.description.empty?
|
69
|
+
#get the requirements
|
70
|
+
reqs=el.elements.select{|e| e.name==ELEM_REQ}
|
71
|
+
reqs.collect!{|r| r.attributes["name"]}
|
72
|
+
spec.requirements=reqs
|
73
|
+
#Get the scenario
|
74
|
+
Dir.chdir(File.dirname(filename)) do
|
75
|
+
spec.scenario=parse_scenario(xmldoc.elements[ELEM_SCENARIO].to_s) if xmldoc.elements[ELEM_SCENARIO]
|
76
|
+
end
|
77
|
+
spec.filename=filename
|
78
|
+
return spec
|
79
|
+
end
|
80
|
+
#Validates the XML file from our point of view.
|
81
|
+
#
|
82
|
+
#Checks for the existence of ELEM_SPEC, ELEM_DESC and ELEM_TITLE and raises ParserError if they're missing.
|
83
|
+
def validate_case xmldoc
|
84
|
+
raise ParserError,"missing #{ELEM_SPEC} element" unless xmldoc.elements[ELEM_SPEC]
|
85
|
+
raise ParserError,"missing #{ELEM_DESC} element" unless xmldoc.elements[ELEM_DESC]
|
86
|
+
raise ParserError,"missing #{ELEM_TITLE} element" unless xmldoc.elements[ELEM_TITLE]
|
87
|
+
end
|
88
|
+
|
89
|
+
#Parses the scenario XML element and returns the Rutema::TestScenario instance
|
90
|
+
def parse_scenario xmltxt
|
91
|
+
@logger.debug("Parsing scenario from #{xmltxt}")
|
92
|
+
scenario=Rutema::TestScenario.new
|
93
|
+
xmldoc=REXML::Document.new( xmltxt )
|
94
|
+
xmldoc.root.attributes.each do |attr,value|
|
95
|
+
add_attribute(scenario,attr,value)
|
96
|
+
end
|
97
|
+
number=0
|
98
|
+
xmldoc.root.elements.each do |el|
|
99
|
+
step=parse_step(el.to_s)
|
100
|
+
if step.step_type=="include_scenario"
|
101
|
+
included_scenario=include_scenario(step)
|
102
|
+
included_scenario.steps.each do |st|
|
103
|
+
@logger.debug("Adding included step #{st}")
|
104
|
+
number+=1
|
105
|
+
st.number=number
|
106
|
+
st.included_in=step.file
|
107
|
+
scenario.add_step(st)
|
108
|
+
end
|
109
|
+
else
|
110
|
+
number+=1
|
111
|
+
step.number=number
|
112
|
+
scenario.add_step(step)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
return scenario
|
116
|
+
end
|
117
|
+
|
118
|
+
#Parses xml and returns the Rutema::TestStep instance
|
119
|
+
def parse_step xmltxt
|
120
|
+
xmldoc=REXML::Document.new( xmltxt )
|
121
|
+
#any step element
|
122
|
+
step=Rutema::TestStep.new()
|
123
|
+
step.ignore=false
|
124
|
+
xmldoc.root.attributes.each do |attr,value|
|
125
|
+
add_attribute(step,attr,value)
|
126
|
+
end
|
127
|
+
step.text=xmldoc.root.text.strip if xmldoc.root.text
|
128
|
+
step.step_type=xmldoc.root.name
|
129
|
+
return step
|
130
|
+
end
|
131
|
+
|
132
|
+
def add_attribute element,attr,value
|
133
|
+
if boolean?(value)
|
134
|
+
element.attribute(attr,eval(value))
|
135
|
+
else
|
136
|
+
element.attribute(attr,value)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def boolean? attribute_value
|
141
|
+
return true if attribute_value=="true" || attribute_value=="false"
|
142
|
+
return false
|
143
|
+
end
|
144
|
+
|
145
|
+
#handles <include_scenario> elements, adding the steps to the current scenario
|
146
|
+
def include_scenario step
|
147
|
+
@logger.debug("Including file from #{step}")
|
148
|
+
raise ParserError,"missing required attribute file in #{step}" unless step.has_file?
|
149
|
+
raise ParserError,"Cannot find #{File.expand_path(step.file)}" unless File.exists?(File.expand_path(step.file))
|
150
|
+
#Load the scenario
|
151
|
+
step.file=File.expand_path(step.file)
|
152
|
+
include_content=File.read(step.file)
|
153
|
+
@logger.debug(include_content)
|
154
|
+
return parse_scenario(include_content)
|
155
|
+
end
|
156
|
+
end
|
157
|
+
#The ExtensibleXMLParser allows you to easily add methods to handle specification elements.
|
158
|
+
#
|
159
|
+
#A method element_foo(step) allows you to add behaviour for foo scenario elements.
|
160
|
+
#
|
161
|
+
#The method will receive a Rutema::TestStep instance.
|
162
|
+
class ExtensibleXMLParser<BaseXMLParser
|
163
|
+
def parse_specification param
|
164
|
+
spec = super(param)
|
165
|
+
#change into the directory the spec is in to handle relative paths correctly
|
166
|
+
Dir.chdir(File.dirname(File.expand_path(spec.filename))) do |path|
|
167
|
+
#iterate through the steps
|
168
|
+
spec.scenario.steps.each do |step|
|
169
|
+
#do we have a method to handle the element?
|
170
|
+
if respond_to?(:"element_#{step.step_type}")
|
171
|
+
begin
|
172
|
+
self.send(:"element_#{step.step_type}",step)
|
173
|
+
rescue
|
174
|
+
raise ParserError, $!.message
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
return spec
|
180
|
+
end
|
181
|
+
end
|
182
|
+
#MinimalXMLParser offers three runnable steps in the scenarios as defined in Rutema::Elements::Minimal
|
183
|
+
class MinimalXMLParser<ExtensibleXMLParser
|
184
|
+
include Rutema::Elements::Minimal
|
185
|
+
end
|
186
|
+
end
|
@@ -1,15 +1,11 @@
|
|
1
1
|
# Copyright (c) 2007-2010 Vassilis Rizopoulos. All rights reserved.
|
2
2
|
$:.unshift File.join(File.dirname(__FILE__),"..","..")
|
3
3
|
require 'yaml'
|
4
|
-
require 'rutema/
|
5
|
-
require 'rutema/model'
|
4
|
+
require 'rutema/models/activerecord'
|
6
5
|
|
7
6
|
module Rutema
|
8
7
|
#The ActiveRecordReporter will store the results of a test run in a database using ActiveRecord.
|
9
|
-
#
|
10
|
-
#The DBMSs supported are dependent on the platform: either SQLite3 (MRI) or h2 (jruby)
|
11
8
|
class ActiveRecordReporter
|
12
|
-
include ActiveRecord
|
13
9
|
#The required keys in this reporter's configuration are:
|
14
10
|
# :db - the database configuration. A Hash with the DB adapter information
|
15
11
|
# :db=>{:database=>"sample.rb"}
|
@@ -18,27 +14,26 @@ module Rutema
|
|
18
14
|
@logger||=Patir.setup_logger
|
19
15
|
database_configuration = definition[:db]
|
20
16
|
raise "No database configuration defined, missing :db configuration key." unless database_configuration
|
21
|
-
ActiveRecord.connect(database_configuration,@logger)
|
17
|
+
Rutema::ActiveRecord.connect(database_configuration,@logger)
|
22
18
|
@logger.info("Reporter #{self.to_s} registered")
|
23
19
|
end
|
24
|
-
|
25
|
-
#We get all the data for a Rutema::ActiveRecord::Model::Run entry in here.
|
20
|
+
#We get all the data for a Rutema::ActiveRecord::Run entry in here.
|
26
21
|
#
|
27
|
-
#If the configuration is given and there is a context defined, this will be YAML-dumped into Rutema::ActiveRecord::
|
22
|
+
#If the configuration is given and there is a context defined, this will be YAML-dumped into Rutema::ActiveRecord::Run#context
|
28
23
|
def report specifications,runner_states,parse_errors,configuration
|
29
|
-
run_entry=ActiveRecord::
|
24
|
+
run_entry=Rutema::ActiveRecord::Run.new
|
30
25
|
if configuration && configuration.context
|
31
26
|
run_entry.context=configuration.context
|
32
27
|
end
|
33
28
|
parse_errors.each do |pe|
|
34
|
-
er=ActiveRecord::
|
29
|
+
er=Rutema::ActiveRecord::ParseError.new()
|
35
30
|
er.filename=pe[:filename]
|
36
31
|
er.error=pe[:error]
|
37
32
|
run_entry.parse_errors<<er
|
38
33
|
end
|
39
34
|
runner_states.compact!
|
40
35
|
runner_states.each do |scenario|
|
41
|
-
sc=ActiveRecord::
|
36
|
+
sc=Rutema::ActiveRecord::Scenario.new
|
42
37
|
sc.name=scenario.sequence_name
|
43
38
|
sc.number=scenario.sequence_id
|
44
39
|
sc.start_time=scenario.start_time
|
@@ -61,7 +56,7 @@ module Rutema
|
|
61
56
|
sc.attended=false
|
62
57
|
end
|
63
58
|
scenario.step_states.each do |number,step|
|
64
|
-
st=ActiveRecord::
|
59
|
+
st=Rutema::ActiveRecord::Step.new
|
65
60
|
st.name=step[:name]
|
66
61
|
st.number=number
|
67
62
|
st.status="#{step[:status]}"
|
@@ -75,17 +70,13 @@ module Rutema
|
|
75
70
|
run_entry.save!
|
76
71
|
"activerecord reporter done"
|
77
72
|
end
|
78
|
-
|
79
|
-
def to_s
|
73
|
+
def to_s#:nodoc:
|
80
74
|
"ActiveRecordReporter"
|
81
75
|
end
|
82
|
-
|
83
76
|
private
|
84
77
|
def sanitize text
|
85
78
|
return text.gsub("\000","") if text
|
86
79
|
return ""
|
87
80
|
end
|
88
|
-
|
89
81
|
end
|
90
|
-
|
91
82
|
end
|
@@ -1,9 +1,9 @@
|
|
1
1
|
# Copyright (c) 2007-2010 Vassilis Rizopoulos. All rights reserved.
|
2
2
|
$:.unshift File.join(File.dirname(__FILE__),"..")
|
3
|
-
require 'rutema/specification'
|
4
|
-
|
5
3
|
module Rutema
|
6
|
-
#Reporter is meant as a base class for reporter classes.
|
4
|
+
#Reporter is meant as a base class for reporter classes. Which means that it is here for ducumentation purposes.
|
5
|
+
#
|
6
|
+
#In order to create act as a Reporter for Rutema a class only need to implement the #report method
|
7
7
|
class Reporter
|
8
8
|
#params should be a Hash containing the parameters used to initialize the class
|
9
9
|
def initialize params
|
@@ -15,10 +15,6 @@ module Rutema
|
|
15
15
|
#
|
16
16
|
#parse_errors is an Array of {:filename,:error} hashes containing the errors encountered by the parser when loading the specifications
|
17
17
|
def report specifications,runner_states,parse_errors,configuration
|
18
|
-
|
19
18
|
end
|
20
19
|
end
|
21
|
-
|
22
|
-
private
|
23
|
-
|
24
20
|
end
|
@@ -1,10 +1,8 @@
|
|
1
1
|
# Copyright (c) 2007-2010 Vassilis Rizopoulos. All rights reserved.
|
2
2
|
$:.unshift File.join(File.dirname(__FILE__),"..","..")
|
3
3
|
require 'net/smtp'
|
4
|
-
require 'rutema/reporter'
|
5
|
-
require 'rutema/specification'
|
6
|
-
require 'rutema/reporters/text'
|
7
4
|
require 'mailfactory'
|
5
|
+
require 'rutema/reporters/text'
|
8
6
|
|
9
7
|
module Rutema
|
10
8
|
#The following configuration keys are used by EmailReporter:
|
@@ -53,8 +51,7 @@ module Rutema
|
|
53
51
|
@verbose||=false
|
54
52
|
@logger.info("Reporter '#{self.to_s}' registered")
|
55
53
|
end
|
56
|
-
|
57
|
-
def to_s
|
54
|
+
def to_s#:nodoc:
|
58
55
|
list=@recipients.join(', ')
|
59
56
|
"EmailReporter - #{@server}:#{@port} from #{@mail.from} to #{list}"
|
60
57
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# Copyright (c) 2007-2010 Vassilis Rizopoulos. All rights reserved.
|
2
|
+
|
1
3
|
module Rutema
|
2
4
|
#This reporter creates a simple text summary of a test run
|
3
5
|
#
|
@@ -9,7 +11,6 @@ module Rutema
|
|
9
11
|
@verbose=params[:verbose] if params
|
10
12
|
@verbose||=false
|
11
13
|
end
|
12
|
-
|
13
14
|
#Returns the text summary
|
14
15
|
#
|
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)
|
@@ -59,7 +60,6 @@ module Rutema
|
|
59
60
|
end
|
60
61
|
msg<<"\nSuccesses:" unless successes.empty?
|
61
62
|
msg<<scenario_summaries(successes,specifications)
|
62
|
-
|
63
63
|
return msg
|
64
64
|
end
|
65
65
|
|
@@ -1,7 +1,7 @@
|
|
1
|
+
# Copyright (c) 2007-2010 Vassilis Rizopoulos. All rights reserved.
|
1
2
|
require 'yaml'
|
2
3
|
|
3
4
|
module Rutema
|
4
|
-
|
5
5
|
module YAML
|
6
6
|
#Experimental reporter used to dump the data of a run on disk
|
7
7
|
#
|
@@ -18,7 +18,6 @@ module Rutema
|
|
18
18
|
@filename||="rutema.yaml"
|
19
19
|
@logger.info("Reporter #{self.to_s} registered")
|
20
20
|
end
|
21
|
-
|
22
21
|
#We get all the data from a test run in here.
|
23
22
|
def report specifications,runner_states,parse_errors,configuration
|
24
23
|
run_entry={}
|