Class: BareTest::Suite
- Inherits:
-
Object
- Object
- BareTest::Suite
- Defined in:
- lib/baretest/suite.rb
Overview
A Suite is a container for multiple assertions. You can give a suite a description, also a suite can contain setup and teardown blocks that are executed before (setup) and after (teardown) every assertion.
Suites can also be nested. Nested suites will inherit setup and teardown.
Constant Summary collapse
- ValidOptions =
A list of valid options Suite::new accepts
[:skip, :requires, :use, :provides, :depends_on, :tags]
Instance Attribute Summary collapse
-
#ancestors ⇒ Object
readonly
An Array containing the suite itself (first element), then its direct parent suite, then that suite’s parent and so on.
-
#assertions ⇒ Object
readonly
All assertions in this suite.
-
#depends_on ⇒ Object
readonly
All things this suite depends on, see Suite::new for more information.
-
#description ⇒ Object
readonly
This suites description.
-
#parent ⇒ Object
readonly
This suites direct parent.
-
#provides ⇒ Object
readonly
All things this suite provides, see Suite::new for more information.
-
#skipped ⇒ Object
readonly
Whether this suite has been manually skipped (either via Suite.new(…, :skip => reason) or via Suite#skip).
-
#suites ⇒ Object
readonly
Nested suites, in the order of definition.
-
#tags ⇒ Object
readonly
All things this suite is tagged with, see Suite::new for more information.
-
#verification_exception_handlers ⇒ Object
readonly
:nodoc:.
Instance Method Summary collapse
-
#ancestry_components ⇒ Object
All setup-components in the order of their definition and nesting (outermost first, innermost last).
-
#ancestry_setup ⇒ Object
All setups in the order of their definition and nesting (outermost first, innermost last).
-
#ancestry_teardown ⇒ Object
All teardowns in the order of their nesting (innermost first, outermost last).
-
#assert(description = nil, opts = nil, &block) ⇒ Object
(also: #guard)
Define an assertion.
-
#component_variant_count ⇒ Object
Returns the number of possible setup variations.
-
#each_component_variant ⇒ Object
Yields all possible permutations of setup components.
-
#first_component_variant {|setups| ... } ⇒ Object
Return only the first of all possible setup variation permutations.
-
#handle_verification_exceptions(*exception_classes, &block) ⇒ Object
Experimental Define handlers for specific exception classes.
-
#id ⇒ Object
An ID, usable for persistence.
-
#initialize(description = nil, parent = nil, opts = nil, &block) ⇒ Suite
constructor
Create a new suite.
-
#inspect ⇒ Object
:nodoc:.
-
#reason(opt = nil) ⇒ Object
The failure/error/skipping/pending reason.
-
#requires(*paths) ⇒ Object
Instruct this suite to require the given files.
-
#setup(component = nil, multiplexed = nil, &block) ⇒ Object
Define a setup block for this suite.
-
#skip(reason = nil) ⇒ Object
Marks this suite as manually skipped.
-
#skipped? ⇒ Boolean
Returns whether this assertion has been marked as manually skipped.
-
#suite(description = nil, *args, &block) ⇒ Object
Define a nested suite.
-
#tagged?(tags) ⇒ Boolean
Returns whether this suite has all the passed tags.
-
#teardown(&block) ⇒ Object
Define a teardown block for this suite.
-
#to_s ⇒ Object
:nodoc:.
-
#update(with_suite) ⇒ Object
Performs a recursive merge with the given suite.
-
#use(*components) ⇒ Object
Instruct this suite to use the given components.
Constructor Details
#initialize(description = nil, parent = nil, opts = nil, &block) ⇒ Suite
Create a new suite.
Arguments:
- description
-
A string with a human readable description of this suite, preferably less than 60 characters and without newlines
- parent
-
The suite that nests this suite. Ancestry plays a role in execution of setup and teardown blocks (all ancestors setups and teardowns are executed too).
- opts
-
An additional options hash.
Keys the options hash accepts:
- :skip
-
Skips the suite if true or a String is passed. If a String is passed, it is used as the reason.
- :requires
-
A string or array of strings with requires that have to be done in order to run this suite. If a require fails, the assertions will all be skipped with reason “Missing dependency”.
- :use
-
A symbol or array of symbols with components this suite should load prior to running.
- :provides
-
A symbol or array of symbols with dependencies this suite resolves, see ‘depends_on’.
- :depends_on
-
A symbol or array of symbols with dependencies of this suite, see ‘provides’.
- :tags
-
A symbol or array of symbols, useful to run only suites having/not having specific tags
- &block
-
The given block is instance evaled and can contain further definition of this assertion. See Suite#suite and Suite#assert.
86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 |
# File 'lib/baretest/suite.rb', line 86 def initialize(description=nil, parent=nil, opts=nil, &block) @description = description @parent = parent @suites = [] # [["description", subsuite, skipped], ["description2", ...], ...] - see Array#assoc @assertions = [] @skipped = false @setup = {nil => []} @components = [] @teardown = [] @verification_exception_handlers = {} if @parent then @ancestors = [self, *@parent.ancestors] @depends_on = @parent.depends_on @tags = @parent. else @ancestors = [self] @depends_on = [] @tags = [] end @provides = [] @reason = [] # skip reason if opts then raise ArgumentError, "Invalid option(s): #{(opts.keys - ValidOptions).inspect}" unless (opts.keys - ValidOptions).empty? skip, requires, use, provides, depends_on, = opts.values_at(*ValidOptions) skip(skip == true ? nil : skip) if skip use(*use) if use requires(*requires) if requires @depends_on |= Array(depends_on) if depends_on @provides |= Array(provides) if provides @tags |= Array() if end instance_eval(&block) if block end |
Instance Attribute Details
#ancestors ⇒ Object (readonly)
An Array containing the suite itself (first element), then its direct parent suite, then that suite’s parent and so on
41 42 43 |
# File 'lib/baretest/suite.rb', line 41 def ancestors @ancestors end |
#assertions ⇒ Object (readonly)
All assertions in this suite
27 28 29 |
# File 'lib/baretest/suite.rb', line 27 def assertions @assertions end |
#depends_on ⇒ Object (readonly)
All things this suite depends on, see Suite::new for more information
44 45 46 |
# File 'lib/baretest/suite.rb', line 44 def depends_on @depends_on end |
#description ⇒ Object (readonly)
This suites description. Toplevel suites usually don’t have a description.
34 35 36 |
# File 'lib/baretest/suite.rb', line 34 def description @description end |
#parent ⇒ Object (readonly)
This suites direct parent. Nil if toplevel suite.
37 38 39 |
# File 'lib/baretest/suite.rb', line 37 def parent @parent end |
#provides ⇒ Object (readonly)
All things this suite provides, see Suite::new for more information
47 48 49 |
# File 'lib/baretest/suite.rb', line 47 def provides @provides end |
#skipped ⇒ Object (readonly)
Whether this suite has been manually skipped (either via Suite.new(…, :skip => reason) or via Suite#skip)
31 32 33 |
# File 'lib/baretest/suite.rb', line 31 def skipped @skipped end |
#suites ⇒ Object (readonly)
Nested suites, in the order of definition
24 25 26 |
# File 'lib/baretest/suite.rb', line 24 def suites @suites end |
#tags ⇒ Object (readonly)
All things this suite is tagged with, see Suite::new for more information
50 51 52 |
# File 'lib/baretest/suite.rb', line 50 def @tags end |
#verification_exception_handlers ⇒ Object (readonly)
:nodoc:
52 53 54 |
# File 'lib/baretest/suite.rb', line 52 def verification_exception_handlers @verification_exception_handlers end |
Instance Method Details
#ancestry_components ⇒ Object
All setup-components in the order of their definition and nesting (outermost first, innermost last)
253 254 255 |
# File 'lib/baretest/suite.rb', line 253 def ancestry_components @parent ? @parent.ancestry_components|@components : @components end |
#ancestry_setup ⇒ Object
All setups in the order of their definition and nesting (outermost first, innermost last)
245 246 247 248 249 |
# File 'lib/baretest/suite.rb', line 245 def ancestry_setup @parent ? @parent.ancestry_setup.merge(@setup) { |k,v1,v2| v1+v2 } : @setup end |
#ancestry_teardown ⇒ Object
All teardowns in the order of their nesting (innermost first, outermost last)
258 259 260 |
# File 'lib/baretest/suite.rb', line 258 def ancestry_teardown ancestors.map { |suite| suite.teardown }.flatten end |
#assert(description = nil, opts = nil, &block) ⇒ Object Also known as: guard
Define an assertion. The block is supposed to return a trueish value (anything but nil or false).
See Assertion for more info.
341 342 343 344 345 346 347 348 349 350 351 352 |
# File 'lib/baretest/suite.rb', line 341 def assert(description=nil, opts=nil, &block) assertion = Assertion.new(self, description, opts, &block) if match = caller.first.match(/^(.*):(\d+)(?::.+)?$/) then file, line = match.captures file = File.(file) if File.exist?(file) then assertion.file = file assertion.line = line.to_i end end @assertions << assertion end |
#component_variant_count ⇒ Object
Returns the number of possible setup variations. See #each_component_variant
300 301 302 |
# File 'lib/baretest/suite.rb', line 300 def component_variant_count ancestry_setup.values_at(*ancestry_components).inject(1) { |r,f| r*f.size } end |
#each_component_variant ⇒ Object
Yields all possible permutations of setup components.
305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 |
# File 'lib/baretest/suite.rb', line 305 def each_component_variant setups = ancestry_setup components = ancestry_components base = setups[nil] if components.empty? yield(base) else setup_in_order = setups.values_at(*components) maximums = setup_in_order.map { |i| i.size } iterations = maximums.inject { |r,f| r*f } || 0 iterations.times do |i| process = maximums.map { |e| i,e=i.divmod(e); e } yield base+setup_in_order.zip(process).map { |variants, current| variants[current] } end end self end |
#first_component_variant {|setups| ... } ⇒ Object
Return only the first of all possible setup variation permutations.
329 330 331 332 333 334 335 |
# File 'lib/baretest/suite.rb', line 329 def first_component_variant setups, *comps = ancestry_setup.values_at(nil, *ancestry_components) setups = setups+comps.map { |comp| comp.first } yield(setups) if block_given? setups end |
#handle_verification_exceptions(*exception_classes, &block) ⇒ Object
Experimental Define handlers for specific exception classes. The handler gets the assertion, the phase and the exception yielded and is expected to return a BareTest::Status.
142 143 144 145 146 147 |
# File 'lib/baretest/suite.rb', line 142 def handle_verification_exceptions(*exception_classes, &block) # :nodoc: exception_classes.each do |exception_class| raise "Already registered a verification exception handler for class #{exception_class}" if @verification_exception_handlers[exception_class] @verification_exception_handlers[exception_class] = block end end |
#id ⇒ Object
An ID, usable for persistence
121 122 123 |
# File 'lib/baretest/suite.rb', line 121 def id @id ||= ancestors.map { |suite| suite.description }.join("\f") end |
#inspect ⇒ Object
:nodoc:
359 360 361 |
# File 'lib/baretest/suite.rb', line 359 def inspect #:nodoc: sprintf "#<%s:%08x %p>", self.class, object_id>>1, @description end |
#reason(opt = nil) ⇒ Object
The failure/error/skipping/pending reason. Returns nil if there’s no reason, a string otherwise Options:
- :default
-
Reason to return if no reason is present
- :separator
-
String used to separate multiple reasons
- :indent
-
A String, the indentation to use. Prefixes every line.
- :first_indent
-
A String, used to indent the first line only (replaces indent).
185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 |
# File 'lib/baretest/suite.rb', line 185 def reason(opt=nil) if opt then default, separator, indent, first_indent = *opt.values_at(:default, :separator, :indent, :first_indent) reason = @reason reason = Array(default) if reason.empty? && default return nil if reason.empty? reason = reason.join(separator || "\n") reason = reason.gsub(/^/, indent) if indent reason = reason.gsub(/^#{Regexp.escape(indent)}/, first_indent) if first_indent reason else @reason.empty? ? nil : @reason.join("\n") end end |
#requires(*paths) ⇒ Object
Instruct this suite to require the given files. The suite is skipped if a file can’t be loaded.
151 152 153 154 155 156 157 158 159 |
# File 'lib/baretest/suite.rb', line 151 def requires(*paths) paths.each do |file| begin require file rescue LoadError => e skip("Missing source file: #{file} (#{e})") end end end |
#setup(component = nil, multiplexed = nil, &block) ⇒ Object
Define a setup block for this suite. The block will be ran before every assertion once, even for nested suites.
264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 |
# File 'lib/baretest/suite.rb', line 264 def setup(component=nil, multiplexed=nil, &block) if component.nil? && block then @setup[nil] << ::BareTest::Setup.new(nil, nil, nil, block) elsif block then @components << component unless @setup.has_key?(component) @setup[component] ||= [] case multiplexed when nil, String @setup[component] << ::BareTest::Setup.new(component, multiplexed, nil, block) when Array multiplexed.each do |substitute| @setup[component] << BareTest::Setup.new(component, substitute.to_s, substitute, block) end when Hash multiplexed.each do |substitute, value| @setup[component] << BareTest::Setup.new(component, substitute, value, block) end else raise TypeError, "multiplexed must be an instance of NilClass, String, Array or Hash, but #{multiplexed.class} given." end elsif component || multiplexed raise ArgumentError, "With component or multiplexed given, a block must be provided too." end @setup end |
#skip(reason = nil) ⇒ Object
Marks this suite as manually skipped.
167 168 169 170 171 |
# File 'lib/baretest/suite.rb', line 167 def skip(reason=nil) @skipped = true @reason |= reason ? Array(reason) : ['Manually skipped'] true end |
#skipped? ⇒ Boolean
Returns whether this assertion has been marked as manually skipped.
174 175 176 |
# File 'lib/baretest/suite.rb', line 174 def skipped? @skipped end |
#suite(description = nil, *args, &block) ⇒ Object
Define a nested suite.
Nested suites inherit setup & teardown methods. Also if an outer suite is skipped, all inner suites are skipped too.
See Suite::new - all arguments are passed to it verbatim, and self is added as parent.
208 209 210 211 212 213 214 215 216 217 |
# File 'lib/baretest/suite.rb', line 208 def suite(description=nil, *args, &block) suite = self.class.new(description, self, *args, &block) existing_suite = @suites.assoc(description) if existing_suite then existing_suite.last.update(suite) else @suites << [description, suite] end suite end |
#tagged?(tags) ⇒ Boolean
Returns whether this suite has all the passed tags
162 163 164 |
# File 'lib/baretest/suite.rb', line 162 def tagged?() (@tags-).empty? end |
#teardown(&block) ⇒ Object
Define a teardown block for this suite. The block will be ran after every assertion once, even for nested suites.
294 295 296 |
# File 'lib/baretest/suite.rb', line 294 def teardown(&block) block ? @teardown << block : @teardown end |
#to_s ⇒ Object
:nodoc:
355 356 357 |
# File 'lib/baretest/suite.rb', line 355 def to_s #:nodoc: sprintf "%s %s", self.class, @description end |
#update(with_suite) ⇒ Object
Performs a recursive merge with the given suite.
Used to merge suites with the same description.
222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 |
# File 'lib/baretest/suite.rb', line 222 def update(with_suite) assertions, setup, teardown, provides, depends_on, skipped, reason, suites = *with_suite.merge_attributes @assertions.concat(assertions) @setup.update(setup) do |k,v1,v2| v1+v2 end @teardown.concat(teardown) @provides |= provides @depends_on |= depends_on @skipped ||= skipped @reason.concat(reason) suites.each { |description, suite| if append_to = @suites.assoc(description) then append_to.last.update(suite) else @suites << [description, suite] end } self end |
#use(*components) ⇒ Object
Instruct this suite to use the given components. The suite is skipped if a component is not available.
127 128 129 130 131 132 133 134 135 136 |
# File 'lib/baretest/suite.rb', line 127 def use(*components) components.each do |name| component = BareTest.component(name) if component then instance_eval(&component) else skip("Missing component: #{name.inspect}") end end end |