rutema 2.0.0.pre5 → 2.0.0.pre6

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,195 +1,208 @@
1
- # Copyright (c) 2007-2015 Vassilis Rizopoulos. All rights reserved.
2
- require 'ostruct'
3
- require_relative 'parser'
4
- require_relative 'reporter'
5
- module Rutema
6
- #This module defines the "configuration directives" used in the configuration of Rutema
7
- #
8
- #Example
9
- #A configuration file needs as a minimum to define which parser to use and which tests to run.
10
- #
11
- #Since rutema configuration files are valid Ruby code, you can use the full power of the Ruby language including require directives
12
- #
13
- # require 'rake'
14
- # configuration.parser={:class=>Rutema::MinimalXMLParser}
15
- # configuration.tests=FileList['all/of/the/tests/**/*.*']
16
- module ConfigurationDirectives
17
- attr_reader :parser,:runner,:tools,:paths,:tests,:context,:check,:setup,:teardown
18
- attr_accessor :reporters
19
- #Adds a hash of values to the tools hash of the configuration
20
- #
21
- #This hash is then accessible in the parser and reporters as a property of the configuration instance
22
- #
23
- #Required keys:
24
- # :name - the name to use for accessing the path in code
25
- #Example:
26
- # configure do |cfg|
27
- # cfg.tool={:name=>"nunit",:path=>"/bin/nunit",:configuration=>{:important=>"info"}}
28
- # end
29
- #
30
- #The path to nunit can then be accessed in the parser as
31
- # @configuration.tools.nunit[:path]
32
- #
33
- #This way you can pass configuration information for the tools you use
34
- def tool= definition
35
- raise ConfigurationException,"required key :name is missing from #{definition}" unless definition[:name]
36
- @tools[definition[:name]]=definition
37
- end
38
- #Adds a path to the paths hash of the configuration
39
- #
40
- #Required keys:
41
- # :name - the name to use for accessing the path in code
42
- # :path - the path
43
- #Example:
44
- # cfg.path={:name=>"sources",:path=>"/src"}
45
- def path= definition
46
- raise ConfigurationException,"required key :name is missing from #{definition}" unless definition[:name]
47
- raise ConfigurationException,"required key :path is missing from #{definition}" unless definition[:path]
48
- @paths[definition[:name]]=definition[:path]
49
- end
50
-
51
- #Path to the setup specification. (optional)
52
- #
53
- #The setup test runs before every test.
54
- def setup= path
55
- @setup=check_path(path)
56
- end
57
-
58
- #Path to the teardown specification. (optional)
59
- #
60
- #The teardown test runs after every test.
61
- def teardown= path
62
- @teardown=check_path(path)
63
- end
64
-
65
- #Path to the check specification. (optional)
66
- #
67
- #The check test runs once in the beginning before all the tests.
68
- #
69
- #If it fails no tests are run.
70
- def check= path
71
- @check=check_path(path)
72
- end
73
-
74
- #Hash values for passing data to the system. It's supposed to be used in the reporters and contain
75
- #values such as version numbers, tester names etc.
76
- def context= definition
77
- @context||=Hash.new
78
- raise ConfigurationException,"Only accepting hash values as context_data" unless definition.kind_of?(Hash)
79
- @context.merge!(definition)
80
- end
81
-
82
- #Adds the specification identifiers available to this instance of Rutema
83
- #
84
- #These will usually be files, but they can be anything.
85
- #Essentially this is an Array of strings that mean something to your parser
86
- def tests= array_of_identifiers
87
- @tests+=array_of_identifiers.map{|f| full_path(f)}
88
- end
89
-
90
- #A hash defining the parser to use.
91
- #
92
- #The hash is passed as is to the parser constructor and each parser should define the necessary configuration keys.
93
- #
94
- #The only required key from the configurator's point fo view is :class which should be set to the fully qualified name of the class to use.
95
- #
96
- #Example:
97
- # cfg.parser={:class=>Rutema::MinimalXMLParser}
98
- def parser= definition
99
- raise ConfigurationException,"required key :class is missing from #{definition}" unless definition[:class]
100
- @parser=definition
101
- end
102
-
103
- #Adds a reporter to the configuration.
104
- #
105
- #As with the parser, the only required configuration key is :class and the definition hash is passed to the class' constructor.
106
- #
107
- #Unlike the parser, you can define multiple reporters.
108
- def reporter= definition
109
- raise ConfigurationException,"required key :class is missing from #{definition}" unless definition[:class]
110
- @reporters[definition[:class]]=definition
111
- end
112
-
113
- def init
114
- @reporters={}
115
- @context={}
116
- @tests=[]
117
- @tools=OpenStruct.new
118
- @paths=OpenStruct.new
119
- end
120
- private
121
- #Checks if a path exists and raises a ConfigurationException if not
122
- def check_path path
123
- path=File.expand_path(path)
124
- raise ConfigurationException,"#{path} does not exist" unless File.exists?(path)
125
- return path
126
- end
127
- #Gives back a string of key=value,key=value for a hash
128
- def definition_string definition
129
- msg=Array.new
130
- definition.each{|k,v| msg<<"#{k}=#{v}"}
131
- return msg.join(",")
132
- end
133
-
134
- def full_path filename
135
- return File.expand_path(filename) if File.exists?(filename)
136
- return filename
137
- end
138
- end
139
-
140
- class ConfigurationException<RuntimeError
141
- end
142
-
143
- class Configuration
144
- include ConfigurationDirectives
145
- attr_reader :filename
146
- def initialize config_file
147
- @filename=config_file
148
- init
149
- load_configuration(@filename)
150
- end
151
-
152
- def configure
153
- if block_given?
154
- yield self
155
- end
156
- end
157
-
158
- #Loads the configuration from a file
159
- #
160
- #Use this to chain configuration files together
161
- #==Example
162
- #Say you have on configuration file "first.rutema" that contains all the generic directives and several others that change only one or two things.
163
- #
164
- #You can import the first.rutema file in the other configurations with
165
- # import("first.rutema")
166
- def import filename
167
- fnm = File.expand_path(filename)
168
- if File.exists?(fnm)
169
- load_configuration(fnm)
170
- else
171
- raise ConfigurationException, "Import error: Can't find #{fnm}"
172
- end
173
- end
174
- private
175
- def load_configuration filename
176
- begin
177
- cfg_txt=File.read(filename)
178
- cwd=File.expand_path(File.dirname(filename))
179
- #evaluate in the working directory to enable relative paths in configuration
180
- Dir.chdir(cwd){eval(cfg_txt,binding(),filename,__LINE__)}
181
- rescue ConfigurationException
182
- #pass it on, do not wrap again
183
- raise
184
- rescue SyntaxError
185
- #Just wrap the exception so we can differentiate
186
- raise ConfigurationException.new,"Syntax error in the configuration file '#{filename}':\n#{$!.message}"
187
- rescue NoMethodError
188
- raise ConfigurationException.new,"Encountered an unknown directive in configuration file '#{filename}':\n#{$!.message}"
189
- rescue
190
- #Just wrap the exception so we can differentiate
191
- raise ConfigurationException.new,"#{$!.message}"
192
- end
193
- end
194
- end
1
+ # Copyright (c) 2007-2015 Vassilis Rizopoulos. All rights reserved.
2
+ require 'ostruct'
3
+ require_relative 'parser'
4
+ require_relative 'reporter'
5
+ module Rutema
6
+ #This module defines the "configuration directives" used in the configuration of Rutema
7
+ #
8
+ #Example
9
+ #A configuration file needs as a minimum to define which parser to use and which tests to run.
10
+ #
11
+ #Since rutema configuration files are valid Ruby code, you can use the full power of the Ruby language including require directives
12
+ #
13
+ # require 'rake'
14
+ # configuration.parser={:class=>Rutema::MinimalXMLParser}
15
+ # configuration.tests=FileList['all/of/the/tests/**/*.*']
16
+ module ConfigurationDirectives
17
+ attr_reader :parser,:runner,:tools,:paths,:tests,:context,:check,:setup,:teardown
18
+ attr_accessor :reporters
19
+ #Adds a hash of values to the tools hash of the configuration
20
+ #
21
+ #This hash is then accessible in the parser and reporters as a property of the configuration instance
22
+ #
23
+ #Required keys:
24
+ # :name - the name to use for accessing the path in code
25
+ #Example:
26
+ # configure do |cfg|
27
+ # cfg.tool={:name=>"nunit",:path=>"/bin/nunit",:configuration=>{:important=>"info"}}
28
+ # end
29
+ #
30
+ #The path to nunit can then be accessed in the parser as
31
+ # @configuration.tools.nunit[:path]
32
+ #
33
+ #This way you can pass configuration information for the tools you use
34
+ def tool= definition
35
+ raise ConfigurationException,"required key :name is missing from #{definition}" unless definition[:name]
36
+ @tools[definition[:name]]=definition
37
+ end
38
+ #Adds a path to the paths hash of the configuration
39
+ #
40
+ #Required keys:
41
+ # :name - the name to use for accessing the path in code
42
+ # :path - the path
43
+ #Example:
44
+ # cfg.path={:name=>"sources",:path=>"/src"}
45
+ def path= definition
46
+ raise ConfigurationException,"required key :name is missing from #{definition}" unless definition[:name]
47
+ raise ConfigurationException,"required key :path is missing from #{definition}" unless definition[:path]
48
+ @paths[definition[:name]]=definition[:path]
49
+ end
50
+
51
+ #Path to the setup specification. (optional)
52
+ #
53
+ #The setup test runs before every test.
54
+ def setup= path
55
+ @setup=check_path(path)
56
+ end
57
+
58
+ #Path to the teardown specification. (optional)
59
+ #
60
+ #The teardown test runs after every test.
61
+ def teardown= path
62
+ @teardown=check_path(path)
63
+ end
64
+
65
+ #Path to the check specification. (optional)
66
+ #
67
+ #The check test runs once in the beginning before all the tests.
68
+ #
69
+ #If it fails no tests are run.
70
+ def check= path
71
+ @check=check_path(path)
72
+ end
73
+
74
+ #Hash values for passing data to the system. It's supposed to be used in the reporters and contain
75
+ #values such as version numbers, tester names etc.
76
+ def context= definition
77
+ @context||=Hash.new
78
+ raise ConfigurationException,"Only accepting hash values as context_data" unless definition.kind_of?(Hash)
79
+ @context.merge!(definition)
80
+ end
81
+
82
+ #Adds the specification identifiers available to this instance of Rutema
83
+ #
84
+ #These will usually be files, but they can be anything.
85
+ #Essentially this is an Array of strings that mean something to your parser
86
+ def tests= array_of_identifiers
87
+ @tests+=array_of_identifiers.map{|f| full_path(f)}
88
+ end
89
+
90
+ #A hash defining the parser to use.
91
+ #
92
+ #The hash is passed as is to the parser constructor and each parser should define the necessary configuration keys.
93
+ #
94
+ #The only required key from the configurator's point fo view is :class which should be set to the fully qualified name of the class to use.
95
+ #
96
+ #Example:
97
+ # cfg.parser={:class=>Rutema::MinimalXMLParser}
98
+ def parser= definition
99
+ raise ConfigurationException,"required key :class is missing from #{definition}" unless definition[:class]
100
+ @parser=definition
101
+ end
102
+
103
+ #A hash defining the runner to use.
104
+ #
105
+ #The hash is passed as is to the runner constructor and each runner should define the necessary configuration keys.
106
+ #
107
+ #The only required key from the configurator's point fo view is :class which should be set to the fully qualified name of the class to use.
108
+ #
109
+ #Example:
110
+ # cfg.runner={:class=>Rutema::Runners::NoOp}
111
+ def runner= definition
112
+ raise ConfigurationException,"required key :class is missing from #{definition}" unless definition[:class]
113
+ @runner=definition
114
+ end
115
+
116
+ #Adds a reporter to the configuration.
117
+ #
118
+ #As with the parser, the only required configuration key is :class and the definition hash is passed to the class' constructor.
119
+ #
120
+ #Unlike the parser, you can define multiple reporters.
121
+ def reporter= definition
122
+ raise ConfigurationException,"required key :class is missing from #{definition}" unless definition[:class]
123
+ @reporters[definition[:class]]=definition
124
+ end
125
+
126
+ def init
127
+ @reporters={}
128
+ @context={}
129
+ @tests=[]
130
+ @tools=OpenStruct.new
131
+ @paths=OpenStruct.new
132
+ end
133
+ private
134
+ #Checks if a path exists and raises a ConfigurationException if not
135
+ def check_path path
136
+ path=File.expand_path(path)
137
+ raise ConfigurationException,"#{path} does not exist" unless File.exist?(path)
138
+ return path
139
+ end
140
+ #Gives back a string of key=value,key=value for a hash
141
+ def definition_string definition
142
+ msg=Array.new
143
+ definition.each{|k,v| msg<<"#{k}=#{v}"}
144
+ return msg.join(",")
145
+ end
146
+
147
+ def full_path filename
148
+ return File.expand_path(filename) if File.exist?(filename)
149
+ return filename
150
+ end
151
+ end
152
+
153
+ class ConfigurationException<RuntimeError
154
+ end
155
+
156
+ class Configuration
157
+ include ConfigurationDirectives
158
+ attr_reader :filename
159
+ def initialize config_file
160
+ @filename=config_file
161
+ init
162
+ load_configuration(@filename)
163
+ end
164
+
165
+ def configure
166
+ if block_given?
167
+ yield self
168
+ end
169
+ end
170
+
171
+ #Loads the configuration from a file
172
+ #
173
+ #Use this to chain configuration files together
174
+ #==Example
175
+ #Say you have on configuration file "first.rutema" that contains all the generic directives and several others that change only one or two things.
176
+ #
177
+ #You can import the first.rutema file in the other configurations with
178
+ # import("first.rutema")
179
+ def import filename
180
+ fnm = File.expand_path(filename)
181
+ if File.exist?(fnm)
182
+ load_configuration(fnm)
183
+ else
184
+ raise ConfigurationException, "Import error: Can't find #{fnm}"
185
+ end
186
+ end
187
+ private
188
+ def load_configuration filename
189
+ begin
190
+ cfg_txt=File.read(filename)
191
+ cwd=File.expand_path(File.dirname(filename))
192
+ #evaluate in the working directory to enable relative paths in configuration
193
+ Dir.chdir(cwd){eval(cfg_txt,binding(),filename,__LINE__)}
194
+ rescue ConfigurationException
195
+ #pass it on, do not wrap again
196
+ raise
197
+ rescue SyntaxError
198
+ #Just wrap the exception so we can differentiate
199
+ raise ConfigurationException.new,"Syntax error in the configuration file '#{filename}':\n#{$!.message}"
200
+ rescue NoMethodError
201
+ raise ConfigurationException.new,"Encountered an unknown directive in configuration file '#{filename}':\n#{$!.message}"
202
+ rescue
203
+ #Just wrap the exception so we can differentiate
204
+ raise ConfigurationException.new,"#{$!.message}"
205
+ end
206
+ end
207
+ end
195
208
  end