| Class | BareTest::Suite |
| In: |
lib/baretest/suite.rb
|
| Parent: | Object |
| ValidOptions | = | [:skip, :requires, :use, :provides, :depends_on, :tags] | A list of valid options Suite::new accepts |
| ancestors | [R] | An Array containing the suite itself (first element), then its direct parent suite, then that suite‘s parent and so on |
| assertions | [R] | All assertions in this suite |
| depends_on | [R] | All things this suite depends on, see Suite::new for more information |
| description | [R] | This suites description. Toplevel suites usually don‘t have a description. |
| parent | [R] | This suites direct parent. Nil if toplevel suite. |
| provides | [R] | All things this suite provides, see Suite::new for more information |
| skipped | [R] | Whether this suite has been manually skipped (either via Suite.new(…, :skip => reason) or via Suite#skip) |
| suites | [R] | Nested suites, in the order of definition |
| tags | [R] | All things this suite is tagged with, see Suite::new for more information |
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. |
# File lib/baretest/suite.rb, line 86
86: def initialize(description=nil, parent=nil, opts=nil, &block)
87: @description = description
88: @parent = parent
89: @suites = [] # [["description", subsuite, skipped], ["description2", ...], ...] - see Array#assoc
90: @assertions = []
91: @skipped = false
92: @setup = {nil => []}
93: @components = []
94: @teardown = []
95: @verification_exception_handlers = {}
96: if @parent then
97: @ancestors = [self, *@parent.ancestors]
98: @depends_on = @parent.depends_on
99: @tags = @parent.tags
100: else
101: @ancestors = [self]
102: @depends_on = []
103: @tags = []
104: end
105: @provides = []
106: @reason = [] # skip reason
107: if opts then
108: raise ArgumentError, "Invalid option(s): #{(opts.keys - ValidOptions).inspect}" unless (opts.keys - ValidOptions).empty?
109: skip, requires, use, provides, depends_on, tags = opts.values_at(*ValidOptions)
110: skip(skip == true ? nil : skip) if skip
111: use(*use) if use
112: requires(*requires) if requires
113: @depends_on |= Array(depends_on) if depends_on
114: @provides |= Array(provides) if provides
115: @tags |= Array(tags) if tags
116: end
117: instance_eval(&block) if block
118: end
All setups in the order of their definition and nesting (outermost first, innermost last)
# File lib/baretest/suite.rb, line 245
245: def ancestry_setup
246: @parent ? @parent.ancestry_setup.merge(@setup) { |k,v1,v2|
247: v1+v2
248: } : @setup
249: end
All teardowns in the order of their nesting (innermost first, outermost last)
# File lib/baretest/suite.rb, line 258
258: def ancestry_teardown
259: ancestors.map { |suite| suite.teardown }.flatten
260: end
Define an assertion. The block is supposed to return a trueish value (anything but nil or false).
See Assertion for more info.
# File lib/baretest/suite.rb, line 341
341: def assert(description=nil, opts=nil, &block)
342: assertion = Assertion.new(self, description, opts, &block)
343: if match = caller.first.match(/^(.*):(\d+)(?::.+)?$/) then
344: file, line = match.captures
345: file = File.expand_path(file)
346: if File.exist?(file) then
347: assertion.file = file
348: assertion.line = line.to_i
349: end
350: end
351: @assertions << assertion
352: end
Returns the number of possible setup variations. See each_component_variant
# File lib/baretest/suite.rb, line 300
300: def component_variant_count
301: ancestry_setup.values_at(*ancestry_components).inject(1) { |r,f| r*f.size }
302: end
Yields all possible permutations of setup components.
# File lib/baretest/suite.rb, line 305
305: def each_component_variant
306: setups = ancestry_setup
307: components = ancestry_components
308: base = setups[nil]
309:
310: if components.empty?
311: yield(base)
312: else
313: setup_in_order = setups.values_at(*components)
314: maximums = setup_in_order.map { |i| i.size }
315: iterations = maximums.inject { |r,f| r*f } || 0
316:
317: iterations.times do |i|
318: process = maximums.map { |e| i,e=i.divmod(e); e }
319: yield base+setup_in_order.zip(process).map { |variants, current|
320: variants[current]
321: }
322: end
323: end
324:
325: self
326: end
Return only the first of all possible setup variation permutations.
# File lib/baretest/suite.rb, line 329
329: def first_component_variant
330: setups, *comps = ancestry_setup.values_at(nil, *ancestry_components)
331: setups = setups+comps.map { |comp| comp.first }
332: yield(setups) if block_given?
333:
334: setups
335: end
An ID, usable for persistence
# File lib/baretest/suite.rb, line 121
121: def id
122: @id ||= ancestors.map { |suite| suite.description }.join("\f")
123: end
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). |
# File lib/baretest/suite.rb, line 185
185: def reason(opt=nil)
186: if opt then
187: default, separator, indent, first_indent =
188: *opt.values_at(:default, :separator, :indent, :first_indent)
189: reason = @reason
190: reason = Array(default) if reason.empty? && default
191: return nil if reason.empty?
192: reason = reason.join(separator || "\n")
193: reason = reason.gsub(/^/, indent) if indent
194: reason = reason.gsub(/^#{Regexp.escape(indent)}/, first_indent) if first_indent
195: reason
196: else
197: @reason.empty? ? nil : @reason.join("\n")
198: end
199: end
Instruct this suite to require the given files. The suite is skipped if a file can‘t be loaded.
# File lib/baretest/suite.rb, line 151
151: def requires(*paths)
152: paths.each do |file|
153: begin
154: require file
155: rescue LoadError => e
156: skip("Missing source file: #{file} (#{e})")
157: end
158: end
159: end
Define a setup block for this suite. The block will be ran before every assertion once, even for nested suites.
# File lib/baretest/suite.rb, line 264
264: def setup(component=nil, multiplexed=nil, &block)
265: if component.nil? && block then
266: @setup[nil] << ::BareTest::Setup.new(nil, nil, nil, block)
267: elsif block then
268: @components << component unless @setup.has_key?(component)
269: @setup[component] ||= []
270:
271: case multiplexed
272: when nil, String
273: @setup[component] << ::BareTest::Setup.new(component, multiplexed, nil, block)
274: when Array
275: multiplexed.each do |substitute|
276: @setup[component] << BareTest::Setup.new(component, substitute.to_s, substitute, block)
277: end
278: when Hash
279: multiplexed.each do |substitute, value|
280: @setup[component] << BareTest::Setup.new(component, substitute, value, block)
281: end
282: else
283: raise TypeError, "multiplexed must be an instance of NilClass, String, Array or Hash, but #{multiplexed.class} given."
284: end
285: elsif component || multiplexed
286: raise ArgumentError, "With component or multiplexed given, a block must be provided too."
287: end
288:
289: @setup
290: end
Returns whether this assertion has been marked as manually skipped.
# File lib/baretest/suite.rb, line 174
174: def skipped?
175: @skipped
176: end
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.
# File lib/baretest/suite.rb, line 208
208: def suite(description=nil, *args, &block)
209: suite = self.class.new(description, self, *args, &block)
210: existing_suite = @suites.assoc(description)
211: if existing_suite then
212: existing_suite.last.update(suite)
213: else
214: @suites << [description, suite]
215: end
216: suite
217: end
Performs a recursive merge with the given suite.
Used to merge suites with the same description.
# File lib/baretest/suite.rb, line 222
222: def update(with_suite)
223: assertions, setup, teardown, provides, depends_on, skipped, reason, suites =
224: *with_suite.merge_attributes
225: @assertions.concat(assertions)
226: @setup.update(setup) do |k,v1,v2| v1+v2 end
227: @teardown.concat(teardown)
228: @provides |= provides
229: @depends_on |= depends_on
230: @skipped ||= skipped
231: @reason.concat(reason)
232: suites.each { |description, suite|
233: if append_to = @suites.assoc(description) then
234: append_to.last.update(suite)
235: else
236: @suites << [description, suite]
237: end
238: }
239:
240: self
241: end
Instruct this suite to use the given components. The suite is skipped if a component is not available.
# File lib/baretest/suite.rb, line 127
127: def use(*components)
128: components.each do |name|
129: component = BareTest.component(name)
130: if component then
131: instance_eval(&component)
132: else
133: skip("Missing component: #{name.inspect}")
134: end
135: end
136: end