test-belt 0.2.1 → 1.0.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/.gitignore +2 -1
- data/Gemfile.lock +23 -0
- data/lib/test_belt/callbacks.rb +22 -0
- data/lib/test_belt/callbacks/case.rb +86 -0
- data/lib/test_belt/callbacks/suite.rb +106 -0
- data/lib/test_belt/callbacks/test.rb +58 -0
- data/lib/test_belt/context.rb +40 -0
- data/lib/test_belt/default_test.rb +18 -0
- data/lib/test_belt/helper.rb +24 -3
- data/lib/test_belt/matchers.rb +29 -0
- data/lib/test_belt/matchers/base.rb +21 -0
- data/lib/test_belt/matchers/have_accessors.rb +23 -0
- data/lib/test_belt/matchers/have_class_methods.rb +40 -0
- data/lib/test_belt/matchers/have_files.rb +38 -0
- data/lib/test_belt/matchers/have_instance_methods.rb +44 -0
- data/lib/test_belt/matchers/have_readers.rb +26 -0
- data/lib/test_belt/matchers/have_writers.rb +30 -0
- data/lib/test_belt/should.rb +76 -0
- data/lib/test_belt/skip.rb +41 -0
- data/lib/test_belt/subject.rb +47 -0
- data/lib/test_belt/version.rb +1 -1
- data/test/callbacks_test.rb +172 -0
- data/test/fixtures/{shoulda_macros/thing.rb → thing.rb} +0 -0
- data/test/helpers_test.rb +175 -0
- data/test/matchers_test.rb +135 -0
- data/test/rake_tasks_test.rb +8 -17
- metadata +33 -31
- data/lib/test_belt/shoulda_macros.rb +0 -9
- data/lib/test_belt/shoulda_macros/classes.rb +0 -105
- data/lib/test_belt/shoulda_macros/context.rb +0 -25
- data/lib/test_belt/shoulda_macros/files.rb +0 -49
- data/lib/test_belt/test_unit.rb +0 -8
- data/lib/test_belt/test_unit/context.rb +0 -71
- data/lib/test_belt/test_unit/runner.rb +0 -48
- data/lib/test_belt/test_unit/test_case.rb +0 -26
- data/test/shoulda_macros/classes_test.rb +0 -58
- data/test/shoulda_macros/context_test.rb +0 -28
- data/test/shoulda_macros/files_test.rb +0 -36
- data/test/test_unit/context_test.rb +0 -65
- data/test/test_unit/runner_test.rb +0 -31
- data/test/test_unit/test_case_test.rb +0 -30
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'test_belt/matchers/have_readers'
|
2
|
+
require 'test_belt/matchers/have_writers'
|
3
|
+
|
4
|
+
module TestBelt::Matchers
|
5
|
+
module HaveAccessors
|
6
|
+
|
7
|
+
def self.included(receiver)
|
8
|
+
receiver.send(:extend, ClassMethods)
|
9
|
+
end
|
10
|
+
|
11
|
+
module ClassMethods
|
12
|
+
def have_accessors(*meths)
|
13
|
+
meths.collect do |meth|
|
14
|
+
[ ::TestBelt::Matchers::HaveReaders::Matcher.new(meth),
|
15
|
+
::TestBelt::Matchers::HaveWriters::Matcher.new(meth)
|
16
|
+
]
|
17
|
+
end.flatten
|
18
|
+
end
|
19
|
+
alias_method :have_accessor, :have_accessors
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module TestBelt::Matchers
|
2
|
+
module HaveClassMethods
|
3
|
+
|
4
|
+
def self.included(receiver)
|
5
|
+
receiver.send(:extend, ClassMethods)
|
6
|
+
end
|
7
|
+
|
8
|
+
module ClassMethods
|
9
|
+
def have_class_methods(*meths)
|
10
|
+
meths.collect do |meth|
|
11
|
+
Matcher.new(meth)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
alias_method :have_class_method, :have_class_methods
|
15
|
+
end
|
16
|
+
|
17
|
+
class Matcher < ::TestBelt::Matchers::Base
|
18
|
+
def initialize(method)
|
19
|
+
unless method.kind_of?(::String) || method.kind_of?(::Symbol)
|
20
|
+
raise ArgumentError, "please specify the method name using a string or symbol"
|
21
|
+
end
|
22
|
+
@method = method
|
23
|
+
end
|
24
|
+
|
25
|
+
def desc
|
26
|
+
"respond to class method ##{@method}"
|
27
|
+
end
|
28
|
+
|
29
|
+
def matches?(subject)
|
30
|
+
@subject = subject
|
31
|
+
subject.class.respond_to?(@method)
|
32
|
+
end
|
33
|
+
|
34
|
+
def fail_message
|
35
|
+
"#{@subject.class.name} does not have the class method ##{@method}"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module TestBelt::Matchers
|
2
|
+
module HaveFiles
|
3
|
+
|
4
|
+
def self.included(receiver)
|
5
|
+
receiver.send(:extend, ClassMethods)
|
6
|
+
end
|
7
|
+
|
8
|
+
module ClassMethods
|
9
|
+
def have_files(*files)
|
10
|
+
files.collect do |file|
|
11
|
+
Matcher.new(file)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
alias_method :have_file, :have_files
|
15
|
+
alias_method :have_directories, :have_files
|
16
|
+
alias_method :have_directory, :have_files
|
17
|
+
end
|
18
|
+
|
19
|
+
class Matcher < ::TestBelt::Matchers::Base
|
20
|
+
def initialize(file_path)
|
21
|
+
@file_path = file_path
|
22
|
+
end
|
23
|
+
|
24
|
+
def desc
|
25
|
+
"have the file path '#{@file_path}'"
|
26
|
+
end
|
27
|
+
|
28
|
+
def matches?(subject)
|
29
|
+
File.exists?(@file_path)
|
30
|
+
end
|
31
|
+
|
32
|
+
def fail_message
|
33
|
+
"'#{@file}' does not exist"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module TestBelt::Matchers
|
2
|
+
module HaveInstanceMethods
|
3
|
+
|
4
|
+
def self.included(receiver)
|
5
|
+
receiver.send(:extend, ClassMethods)
|
6
|
+
end
|
7
|
+
|
8
|
+
module ClassMethods
|
9
|
+
def have_instance_methods(*meths)
|
10
|
+
meths.collect do |meth|
|
11
|
+
Matcher.new(meth)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
alias_method :have_instance_method, :have_instance_methods
|
15
|
+
end
|
16
|
+
|
17
|
+
class Matcher < ::TestBelt::Matchers::Base
|
18
|
+
def initialize(method)
|
19
|
+
unless method.kind_of?(::String) || method.kind_of?(::Symbol)
|
20
|
+
raise ArgumentError, "please specify the method name using a string or symbol"
|
21
|
+
end
|
22
|
+
@method = method
|
23
|
+
end
|
24
|
+
|
25
|
+
def method_type
|
26
|
+
"instance method"
|
27
|
+
end
|
28
|
+
|
29
|
+
def desc
|
30
|
+
"respond to #{method_type} ##{@method}"
|
31
|
+
end
|
32
|
+
|
33
|
+
def matches?(subject)
|
34
|
+
@subject = subject
|
35
|
+
subject.respond_to?(@method)
|
36
|
+
end
|
37
|
+
|
38
|
+
def fail_message
|
39
|
+
"#{@subject.class.name} does not have instance method ##{@method}"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'test_belt/matchers/have_instance_methods'
|
2
|
+
|
3
|
+
module TestBelt::Matchers
|
4
|
+
module HaveReaders
|
5
|
+
|
6
|
+
def self.included(receiver)
|
7
|
+
receiver.send(:extend, ClassMethods)
|
8
|
+
end
|
9
|
+
|
10
|
+
module ClassMethods
|
11
|
+
def have_readers(*meths)
|
12
|
+
meths.collect do |meth|
|
13
|
+
Matcher.new(meth)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
alias_method :have_reader, :have_readers
|
17
|
+
end
|
18
|
+
|
19
|
+
class Matcher < ::TestBelt::Matchers::HaveInstanceMethods::Matcher
|
20
|
+
def method_type
|
21
|
+
"reader"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'test_belt/matchers/have_instance_methods'
|
2
|
+
|
3
|
+
module TestBelt::Matchers
|
4
|
+
module HaveWriters
|
5
|
+
|
6
|
+
def self.included(receiver)
|
7
|
+
receiver.send(:extend, ClassMethods)
|
8
|
+
end
|
9
|
+
|
10
|
+
module ClassMethods
|
11
|
+
def have_writers(*meths)
|
12
|
+
meths.collect do |meth|
|
13
|
+
Matcher.new(meth)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
alias_method :have_writer, :have_writers
|
17
|
+
end
|
18
|
+
|
19
|
+
class Matcher < ::TestBelt::Matchers::HaveInstanceMethods::Matcher
|
20
|
+
def initialize(method)
|
21
|
+
super("#{method}=")
|
22
|
+
end
|
23
|
+
|
24
|
+
def method_type
|
25
|
+
"writer"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module TestBelt
|
2
|
+
module Should
|
3
|
+
|
4
|
+
# This defines a class method 'should' that takes a string
|
5
|
+
# describing the test and a block that executes the test. The
|
6
|
+
# test will be named:
|
7
|
+
# "test: #{context} should #{description}."
|
8
|
+
|
9
|
+
# Usage:
|
10
|
+
# class SomeTest < Test::Unit::TestCase
|
11
|
+
# extend TestBelt::Should
|
12
|
+
# end
|
13
|
+
|
14
|
+
def should(matchers_or_desc, &block)
|
15
|
+
tests = should_tests(matchers_or_desc, &block)
|
16
|
+
context = should_context
|
17
|
+
|
18
|
+
each_should(context, tests) do |name, code|
|
19
|
+
define_method(name) do
|
20
|
+
begin
|
21
|
+
self.class._testbelt_setups.each {|sb| instance_eval(&sb)}
|
22
|
+
instance_eval(&code)
|
23
|
+
ensure
|
24
|
+
self.class._testbelt_teardowns.reverse.each {|tb| instance_eval(&tb)}
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# This method is identical to the should version, however this one does
|
31
|
+
# nothing but skip any tests described or matched on
|
32
|
+
def should_eventually(matchers_or_desc, &block)
|
33
|
+
tests = should_tests(matchers_or_desc, &block)
|
34
|
+
context = should_context
|
35
|
+
|
36
|
+
each_should(context, tests) do |name, code|
|
37
|
+
define_method(name) { skip }
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def each_should(context, tests)
|
44
|
+
tests.each do |t|
|
45
|
+
yield should_test_name(context, t[0]), t[1]
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def should_tests(matchers_or_desc, &block)
|
50
|
+
unless matchers_or_desc.kind_of?(::Array) || block_given?
|
51
|
+
raise ArgumentError, "either specify a test block and description or a set of matchers"
|
52
|
+
end
|
53
|
+
|
54
|
+
if matchers_or_desc.kind_of?(::Array)
|
55
|
+
matchers_or_desc.collect do |matcher|
|
56
|
+
[matcher.desc, Proc.new {assert_matcher(matcher)}]
|
57
|
+
end
|
58
|
+
else
|
59
|
+
[[matchers_or_desc.to_s, block]]
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def should_context
|
64
|
+
'' # TODO: context stuff needs to be added to test name
|
65
|
+
end
|
66
|
+
|
67
|
+
def should_test_name(context, name)
|
68
|
+
test_name = ["test:", "should", "#{name}. "].flatten.join(' ').to_sym
|
69
|
+
if instance_methods.include?(test_name.to_s)
|
70
|
+
warn " * WARNING: '#{test_name}' is already defined"
|
71
|
+
end
|
72
|
+
test_name
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Test::Unit
|
2
|
+
class TestCase
|
3
|
+
|
4
|
+
alias_method(:orig_add_error, :add_error)
|
5
|
+
def add_error(*args, &block)
|
6
|
+
unless args.first.kind_of?(::TestBelt::TestSkipped)
|
7
|
+
orig_add_error *args, &block
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
module TestBelt
|
15
|
+
class TestSkipped < Exception; end
|
16
|
+
|
17
|
+
module Skip
|
18
|
+
|
19
|
+
# Sometimes while testing, you may wish to define some tests but not run
|
20
|
+
# them just yet. LeftRight provides some nice UI for dealing with skipped
|
21
|
+
# tests. Test Belt provides a handy 'skip' method for test cases. Use
|
22
|
+
# this method to skip individual tests. If you wish to skip a whole
|
23
|
+
# context of tests, add the skip method to a before [[Callback]] and all
|
24
|
+
# the tests will be skipped for the class. Since callbacks inherit, any
|
25
|
+
# subclassed tests will be skipped as well.
|
26
|
+
|
27
|
+
# Usage:
|
28
|
+
# class SomeTest < Test::Unit::TestCase
|
29
|
+
# include TestBelt::Skip
|
30
|
+
# end
|
31
|
+
|
32
|
+
def skip(halt_test=true)
|
33
|
+
if defined? ::LeftRight
|
34
|
+
::LeftRight.state.skip = true
|
35
|
+
::LeftRight.state.skipped_count += 1
|
36
|
+
end
|
37
|
+
raise ::TestBelt::TestSkipped if halt_test
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module TestBelt
|
2
|
+
module Subject
|
3
|
+
|
4
|
+
# This provides a class method 'subject' on a TestCase. Use this method
|
5
|
+
# to initialize the subject of the TestCase. This may be a model, class,
|
6
|
+
# or whatever. The subject method takes a block and the result of the
|
7
|
+
# block is used as the subject value. Note, the block is for every test
|
8
|
+
# withing the scope of the TestCase instance, and after any setup/before
|
9
|
+
# callbacks. The subject value is available using the 'subject instance
|
10
|
+
# method on the TestCase. Any sub-classed TestCase that does not define
|
11
|
+
# it's own subject will inherite the subject of it's super class.
|
12
|
+
|
13
|
+
# Usage:
|
14
|
+
# class SomeTest < Test::Unit::TestCase
|
15
|
+
# include TestBelt::Subject
|
16
|
+
# end
|
17
|
+
|
18
|
+
def self.included(receiver)
|
19
|
+
receiver.send(:extend, ClassMethods)
|
20
|
+
receiver.send(:include, InstanceMethods)
|
21
|
+
end
|
22
|
+
|
23
|
+
module ClassMethods
|
24
|
+
def subject(&block)
|
25
|
+
raise ArgumentError, "please provide a subject block" unless block_given?
|
26
|
+
@_testbelt_subject = block
|
27
|
+
end
|
28
|
+
|
29
|
+
def _testbelt_subject
|
30
|
+
@_testbelt_subject || begin
|
31
|
+
superclass._testbelt_subject
|
32
|
+
rescue NoMethodError
|
33
|
+
nil
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
module InstanceMethods
|
39
|
+
def subject
|
40
|
+
@_testbelt_subject ||= if (sb = self.class._testbelt_subject)
|
41
|
+
instance_eval(&sb)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
data/lib/test_belt/version.rb
CHANGED
@@ -0,0 +1,172 @@
|
|
1
|
+
require "test/helper"
|
2
|
+
|
3
|
+
module TestBelt
|
4
|
+
|
5
|
+
class TestCallbacksTest < Test::Unit::TestCase
|
6
|
+
include TestBelt
|
7
|
+
|
8
|
+
context "A test with callbacks"
|
9
|
+
subject { 'subject' }
|
10
|
+
before {
|
11
|
+
@before = 'before'
|
12
|
+
@before_subject = subject
|
13
|
+
@expected_after = 'after'
|
14
|
+
}
|
15
|
+
setup {
|
16
|
+
@setup = 'setup'
|
17
|
+
@setup_subject = subject
|
18
|
+
@expected_teardown = 'teardown'
|
19
|
+
}
|
20
|
+
|
21
|
+
should "run the it's callbacks appropriately" do
|
22
|
+
assert_equal 'before', @before
|
23
|
+
assert_equal 'setup', @setup
|
24
|
+
assert_equal 'subject', @before_subject
|
25
|
+
assert_equal 'subject', @setup_subject
|
26
|
+
end
|
27
|
+
|
28
|
+
after {
|
29
|
+
assert_equal 'after', @expected_after
|
30
|
+
}
|
31
|
+
teardown {
|
32
|
+
assert_equal 'teardown', @expected_teardown
|
33
|
+
}
|
34
|
+
end
|
35
|
+
class RootCallbacksTest < Test::Unit::TestCase
|
36
|
+
include TestBelt
|
37
|
+
|
38
|
+
before {
|
39
|
+
@before = ['root']
|
40
|
+
}
|
41
|
+
after {
|
42
|
+
assert_equal ['nested nested', 'nested'], @after
|
43
|
+
}
|
44
|
+
end
|
45
|
+
class NestedCallbacksTest < RootCallbacksTest
|
46
|
+
before {
|
47
|
+
@before << 'nested'
|
48
|
+
}
|
49
|
+
after {
|
50
|
+
@after << 'nested'
|
51
|
+
}
|
52
|
+
end
|
53
|
+
class NestedNestedCallbacksTest < NestedCallbacksTest
|
54
|
+
should "call it's nested callbacks in order" do
|
55
|
+
assert_equal ['root', 'nested'], @before
|
56
|
+
end
|
57
|
+
|
58
|
+
after {
|
59
|
+
@after = ['nested nested']
|
60
|
+
}
|
61
|
+
end
|
62
|
+
|
63
|
+
|
64
|
+
|
65
|
+
|
66
|
+
class CaseCallbacksTest < Test::Unit::TestCase
|
67
|
+
include TestBelt
|
68
|
+
|
69
|
+
context "A test with case callbacks"
|
70
|
+
|
71
|
+
before_once {
|
72
|
+
@expected_after = 'after'
|
73
|
+
if (@@before_once rescue nil)
|
74
|
+
@before_once_fail = true
|
75
|
+
end
|
76
|
+
@@before_once = true
|
77
|
+
}
|
78
|
+
setup_once {
|
79
|
+
@expected_teardown = 'teardown'
|
80
|
+
if (@@setup_once rescue nil)
|
81
|
+
@setup_once_fail = true
|
82
|
+
end
|
83
|
+
@@setup_once = true
|
84
|
+
}
|
85
|
+
|
86
|
+
should "run the case callbacks just once" do
|
87
|
+
assert true
|
88
|
+
end
|
89
|
+
should "not run the case callbacks more than once" do
|
90
|
+
assert true
|
91
|
+
end
|
92
|
+
|
93
|
+
after_once {
|
94
|
+
if (@@after_once rescue nil)
|
95
|
+
@after_once_fail = true
|
96
|
+
end
|
97
|
+
@@after_once = true
|
98
|
+
raise 'looks like before_once did not run' unless @expected_after == 'after'
|
99
|
+
raise "before_once did not run once" unless (@@before_once rescue nil)
|
100
|
+
raise "before_once ran more than once" unless @before_once_fail.nil?
|
101
|
+
raise "after_once did not run once" unless (@@after_once rescue nil)
|
102
|
+
raise "after_once ran more than once" unless @after_once_fail.nil?
|
103
|
+
}
|
104
|
+
teardown_once {
|
105
|
+
if (@@teardown_once rescue nil)
|
106
|
+
@teardown_once_fail = true
|
107
|
+
end
|
108
|
+
@@teardown_once = true
|
109
|
+
raise 'looks like setup_once did not run' unless @expected_teardown == 'teardown'
|
110
|
+
raise "setup_once did not run once" unless (@@setup_once rescue nil)
|
111
|
+
raise "setup_once ran more than once" unless @setup_once_fail.nil?
|
112
|
+
raise "teardown_once did not run once" unless (@@teardown_once rescue nil)
|
113
|
+
raise "teardown_once ran more than once" unless @teardown_once_fail.nil?
|
114
|
+
}
|
115
|
+
end
|
116
|
+
|
117
|
+
|
118
|
+
|
119
|
+
|
120
|
+
class SuiteCallbacksTest < Test::Unit::TestCase
|
121
|
+
include TestBelt
|
122
|
+
|
123
|
+
context "A test with suite callbacks"
|
124
|
+
|
125
|
+
suite_started {
|
126
|
+
@started_assert = true
|
127
|
+
@expected_finished = 'finished'
|
128
|
+
if (@@started rescue nil)
|
129
|
+
@started_fail = true
|
130
|
+
end
|
131
|
+
@@started = true
|
132
|
+
}
|
133
|
+
on_suite_started {
|
134
|
+
@on_started_assert = true
|
135
|
+
@expected_on_finished = 'on finished'
|
136
|
+
if (@@on_started rescue nil)
|
137
|
+
@on_started_fail = true
|
138
|
+
end
|
139
|
+
@@on_started = true
|
140
|
+
}
|
141
|
+
|
142
|
+
should "run the suite callbacks just once" do
|
143
|
+
assert true
|
144
|
+
end
|
145
|
+
should "not run the suite callbacks more than once" do
|
146
|
+
assert true
|
147
|
+
end
|
148
|
+
|
149
|
+
suite_finished {
|
150
|
+
if (@@finished rescue nil)
|
151
|
+
@finished_fail = true
|
152
|
+
end
|
153
|
+
@@finished = true
|
154
|
+
raise 'looks like suite_started did not run' unless @expected_finished == 'finished'
|
155
|
+
raise "suite_started did not run once" unless (@@started rescue nil)
|
156
|
+
raise "suite_started ran more than once" unless @started_fail.nil?
|
157
|
+
raise "suite_finished did not run once" unless (@@finished rescue nil)
|
158
|
+
raise "suite_finished ran more than once" unless @finished_fail.nil?
|
159
|
+
}
|
160
|
+
on_suite_finished {
|
161
|
+
if (@@on_finished rescue nil)
|
162
|
+
@on_finished_fail = true
|
163
|
+
end
|
164
|
+
@@on_finished = true
|
165
|
+
raise 'looks like on_suite_started did not run' unless @expected_on_finished == 'on finished'
|
166
|
+
raise "on_suite_started did not run once" unless (@@on_started rescue nil)
|
167
|
+
raise "on_suite_started ran more than once" unless @on_started_fail.nil?
|
168
|
+
raise "on_suite_finished did not run once" unless (@@on_finished rescue nil)
|
169
|
+
raise "on_suite_finished ran more than once" unless @on_finished_fail.nil?
|
170
|
+
}
|
171
|
+
end
|
172
|
+
end
|