test_bench-fixture 1.0.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/test_bench/fixture.rb +18 -0
- data/lib/test_bench/fixture/assertion_failure.rb +19 -0
- data/lib/test_bench/fixture/controls.rb +10 -0
- data/lib/test_bench/fixture/controls/caller_location.rb +72 -0
- data/lib/test_bench/fixture/controls/error.rb +47 -0
- data/lib/test_bench/fixture/controls/error/backtrace.rb +31 -0
- data/lib/test_bench/fixture/controls/fixture.rb +85 -0
- data/lib/test_bench/fixture/controls/output.rb +64 -0
- data/lib/test_bench/fixture/controls/result.rb +23 -0
- data/lib/test_bench/fixture/controls/test_file.rb +72 -0
- data/lib/test_bench/fixture/error_policy.rb +75 -0
- data/lib/test_bench/fixture/fixture.rb +149 -0
- data/lib/test_bench/fixture/output.rb +56 -0
- data/lib/test_bench/fixture/output/log.rb +109 -0
- data/lib/test_bench/fixture/output/multiple.rb +42 -0
- data/lib/test_bench/fixture/output/null.rb +9 -0
- data/lib/test_bench/fixture/output/substitute.rb +141 -0
- data/lib/test_bench/fixture/session.rb +246 -0
- data/lib/test_bench/fixture/session/substitute.rb +126 -0
- data/lib/test_bench/fixture/session/substitute/match_tests.rb +131 -0
- metadata +76 -0
@@ -0,0 +1,246 @@
|
|
1
|
+
module TestBench
|
2
|
+
module Fixture
|
3
|
+
class Session
|
4
|
+
Error = Class.new(RuntimeError)
|
5
|
+
|
6
|
+
def assertion_counter
|
7
|
+
@assertion_counter ||= 0
|
8
|
+
end
|
9
|
+
attr_writer :assertion_counter
|
10
|
+
|
11
|
+
def error_counter
|
12
|
+
@error_counter ||= 0
|
13
|
+
end
|
14
|
+
attr_writer :error_counter
|
15
|
+
|
16
|
+
def started
|
17
|
+
instance_variable_defined?(:@started) ?
|
18
|
+
@started :
|
19
|
+
@started = false
|
20
|
+
end
|
21
|
+
attr_writer :started
|
22
|
+
alias_method :started?, :started
|
23
|
+
|
24
|
+
def finished
|
25
|
+
instance_variable_defined?(:@finished) ?
|
26
|
+
@finished :
|
27
|
+
@finished = false
|
28
|
+
end
|
29
|
+
attr_writer :finished
|
30
|
+
alias_method :finished?, :finished
|
31
|
+
|
32
|
+
def error_policy
|
33
|
+
@error_policy ||= ErrorPolicy::Build.(:rescue_assert)
|
34
|
+
end
|
35
|
+
attr_writer :error_policy
|
36
|
+
|
37
|
+
def output
|
38
|
+
@output ||= Output::Substitute.build
|
39
|
+
end
|
40
|
+
attr_writer :output
|
41
|
+
|
42
|
+
def self.build(output: nil, error_policy: nil)
|
43
|
+
instance = new
|
44
|
+
|
45
|
+
if output.nil?
|
46
|
+
Output::Log.configure(instance)
|
47
|
+
else
|
48
|
+
instance.output = output
|
49
|
+
end
|
50
|
+
|
51
|
+
ErrorPolicy.configure(instance, policy: error_policy)
|
52
|
+
|
53
|
+
instance
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.configure(receiver, session: nil, output: nil, error_policy: nil, attr_name: nil)
|
57
|
+
attr_name ||= :session
|
58
|
+
|
59
|
+
if session.nil?
|
60
|
+
instance = build(output: output, error_policy: error_policy)
|
61
|
+
else
|
62
|
+
instance = session
|
63
|
+
end
|
64
|
+
|
65
|
+
receiver.public_send(:"#{attr_name}=", instance)
|
66
|
+
|
67
|
+
instance
|
68
|
+
end
|
69
|
+
|
70
|
+
def start
|
71
|
+
if started
|
72
|
+
raise Error, "Session has already been started"
|
73
|
+
end
|
74
|
+
|
75
|
+
self.started = true
|
76
|
+
|
77
|
+
output.start
|
78
|
+
end
|
79
|
+
|
80
|
+
def finish
|
81
|
+
if finished
|
82
|
+
raise Error, "Session has already finished"
|
83
|
+
end
|
84
|
+
|
85
|
+
self.finished = true
|
86
|
+
|
87
|
+
result = !failed?
|
88
|
+
|
89
|
+
output.finish(result)
|
90
|
+
|
91
|
+
result
|
92
|
+
end
|
93
|
+
|
94
|
+
def comment(text)
|
95
|
+
output.comment(text)
|
96
|
+
end
|
97
|
+
|
98
|
+
def error(error)
|
99
|
+
fail!
|
100
|
+
|
101
|
+
output.error(error)
|
102
|
+
|
103
|
+
error_policy.(error)
|
104
|
+
end
|
105
|
+
|
106
|
+
def assert(value, caller_location: nil)
|
107
|
+
caller_location ||= caller_locations.first
|
108
|
+
|
109
|
+
result = value ? true : false
|
110
|
+
|
111
|
+
self.assertion_counter += 1
|
112
|
+
|
113
|
+
output.assert(result, caller_location)
|
114
|
+
|
115
|
+
unless result
|
116
|
+
self.error_counter += 1
|
117
|
+
|
118
|
+
assertion_failure = AssertionFailure.build(caller_location)
|
119
|
+
raise assertion_failure
|
120
|
+
end
|
121
|
+
|
122
|
+
result
|
123
|
+
end
|
124
|
+
|
125
|
+
def assert_block(caller_location: nil, &block)
|
126
|
+
caller_location ||= caller_locations.first
|
127
|
+
|
128
|
+
previous_error_counter = self.error_counter
|
129
|
+
previous_assertion_counter = self.assertion_counter
|
130
|
+
|
131
|
+
output.enter_assert_block(caller_location)
|
132
|
+
|
133
|
+
begin
|
134
|
+
block.()
|
135
|
+
|
136
|
+
ensure
|
137
|
+
if self.error_counter > previous_error_counter
|
138
|
+
result = false
|
139
|
+
elsif self.assertion_counter == previous_assertion_counter
|
140
|
+
result = false
|
141
|
+
else
|
142
|
+
result = true
|
143
|
+
end
|
144
|
+
|
145
|
+
output.exit_assert_block(caller_location, result)
|
146
|
+
|
147
|
+
current_exception = $!
|
148
|
+
if current_exception.nil? || current_exception.instance_of?(AssertionFailure)
|
149
|
+
assert(result, caller_location: caller_location)
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
def load(path)
|
155
|
+
output.enter_file(path)
|
156
|
+
|
157
|
+
action = proc { Kernel.load(path) }
|
158
|
+
|
159
|
+
result = evaluate(action)
|
160
|
+
|
161
|
+
output.exit_file(path, result)
|
162
|
+
|
163
|
+
result
|
164
|
+
end
|
165
|
+
|
166
|
+
def test(title=nil, &block)
|
167
|
+
if block.nil?
|
168
|
+
output.skip_test(title)
|
169
|
+
return
|
170
|
+
end
|
171
|
+
|
172
|
+
output.start_test(title)
|
173
|
+
|
174
|
+
evaluate(block) do |result|
|
175
|
+
output.finish_test(title, result)
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
def context(title=nil, &block)
|
180
|
+
if block.nil?
|
181
|
+
output.skip_context(title)
|
182
|
+
return
|
183
|
+
end
|
184
|
+
|
185
|
+
output.enter_context(title)
|
186
|
+
|
187
|
+
evaluate(block) do |result|
|
188
|
+
output.exit_context(title, result)
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
def fixture(fixture, &block)
|
193
|
+
if block.nil? && !fixture.respond_to?(:call)
|
194
|
+
raise Error, "Block must be given when a fixture does not respond to #call"
|
195
|
+
end
|
196
|
+
|
197
|
+
actions = []
|
198
|
+
|
199
|
+
if fixture.respond_to?(:call)
|
200
|
+
actions << fixture
|
201
|
+
end
|
202
|
+
|
203
|
+
unless block.nil?
|
204
|
+
actions << proc { block.(fixture) }
|
205
|
+
end
|
206
|
+
|
207
|
+
output.start_fixture(fixture)
|
208
|
+
|
209
|
+
action = proc { actions.each(&:call) }
|
210
|
+
result = evaluate(action)
|
211
|
+
|
212
|
+
output.finish_fixture(fixture, result)
|
213
|
+
|
214
|
+
result
|
215
|
+
end
|
216
|
+
|
217
|
+
def evaluate(action, &block)
|
218
|
+
previous_error_counter = self.error_counter
|
219
|
+
|
220
|
+
begin
|
221
|
+
action.()
|
222
|
+
|
223
|
+
rescue => error
|
224
|
+
error(error)
|
225
|
+
|
226
|
+
ensure
|
227
|
+
current_exception = $!
|
228
|
+
|
229
|
+
result = error_counter == previous_error_counter
|
230
|
+
|
231
|
+
block.(result, current_exception) unless block.nil?
|
232
|
+
end
|
233
|
+
|
234
|
+
result
|
235
|
+
end
|
236
|
+
|
237
|
+
def fail!
|
238
|
+
self.error_counter += 1
|
239
|
+
end
|
240
|
+
|
241
|
+
def failed?
|
242
|
+
error_counter.nonzero? ? true : false
|
243
|
+
end
|
244
|
+
end
|
245
|
+
end
|
246
|
+
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
module TestBench
|
2
|
+
module Fixture
|
3
|
+
class Session
|
4
|
+
module Substitute
|
5
|
+
def self.build
|
6
|
+
Session.new
|
7
|
+
end
|
8
|
+
|
9
|
+
class Session < Session
|
10
|
+
Error = Class.new(RuntimeError)
|
11
|
+
|
12
|
+
attr_accessor :load_failure
|
13
|
+
|
14
|
+
def commented?(text)
|
15
|
+
output.recorded?(:comment) do |t|
|
16
|
+
t == text
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def asserted?(result=nil, caller_location: nil)
|
21
|
+
output.recorded?(:assert) do |r, cl|
|
22
|
+
(result.nil? || r == result) &&
|
23
|
+
(caller_location.nil? || cl == caller_location)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def load_failure!
|
28
|
+
self.load_failure = true
|
29
|
+
end
|
30
|
+
|
31
|
+
def load(path)
|
32
|
+
result = load_failure ? false : true
|
33
|
+
|
34
|
+
output.enter_file(path)
|
35
|
+
|
36
|
+
output.exit_file(path, result)
|
37
|
+
|
38
|
+
result
|
39
|
+
end
|
40
|
+
|
41
|
+
def loaded?(path)
|
42
|
+
output.recorded?(:exit_file) do |p|
|
43
|
+
p == path
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def test?(title)
|
48
|
+
output.recorded?(:finish_test) do |t|
|
49
|
+
t == title
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def context?(title)
|
54
|
+
output.recorded?(:exit_context) do |t|
|
55
|
+
t == title
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def fixture?(fixture)
|
60
|
+
output.recorded?(:finish_fixture) do |f|
|
61
|
+
f == fixture
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def passed?(*titles)
|
66
|
+
!pass(*titles).nil?
|
67
|
+
end
|
68
|
+
|
69
|
+
def failed?(*titles)
|
70
|
+
return super if titles.empty?
|
71
|
+
|
72
|
+
!failure(*titles).nil?
|
73
|
+
end
|
74
|
+
|
75
|
+
def one_pass(*titles)
|
76
|
+
passes = passes(*titles)
|
77
|
+
|
78
|
+
if passes.count > 1
|
79
|
+
raise Error, "Multiple passing tests match (Titles: #{titles.inspect})"
|
80
|
+
end
|
81
|
+
|
82
|
+
passes.first
|
83
|
+
end
|
84
|
+
|
85
|
+
def one_passed?(*titles)
|
86
|
+
one_pass(*titles) ? true : false
|
87
|
+
end
|
88
|
+
|
89
|
+
def pass(*titles)
|
90
|
+
passes(*titles).first
|
91
|
+
end
|
92
|
+
|
93
|
+
def passes(*titles)
|
94
|
+
match_tests(*titles, result: true)
|
95
|
+
end
|
96
|
+
|
97
|
+
def one_failure(*titles)
|
98
|
+
failures = failures(*titles)
|
99
|
+
|
100
|
+
if failures.count > 1
|
101
|
+
raise Error, "Multiple failing tests match (Titles: #{titles.inspect})"
|
102
|
+
end
|
103
|
+
|
104
|
+
failures.first
|
105
|
+
end
|
106
|
+
|
107
|
+
def one_failed?(*titles)
|
108
|
+
one_failure(*titles) ? true : false
|
109
|
+
end
|
110
|
+
|
111
|
+
def failure(*titles)
|
112
|
+
failures(*titles).first
|
113
|
+
end
|
114
|
+
|
115
|
+
def failures(*titles)
|
116
|
+
match_tests(*titles, result: false)
|
117
|
+
end
|
118
|
+
|
119
|
+
def match_tests(*titles, result: nil)
|
120
|
+
MatchTests.(self, *titles, result: result)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
@@ -0,0 +1,131 @@
|
|
1
|
+
module TestBench
|
2
|
+
module Fixture
|
3
|
+
class Session
|
4
|
+
module Substitute
|
5
|
+
class MatchTests
|
6
|
+
include Output
|
7
|
+
|
8
|
+
attr_reader :output_substitute
|
9
|
+
attr_reader :patterns
|
10
|
+
attr_reader :result
|
11
|
+
|
12
|
+
def matches
|
13
|
+
@matches ||= []
|
14
|
+
end
|
15
|
+
|
16
|
+
def stack
|
17
|
+
@stack ||= []
|
18
|
+
end
|
19
|
+
|
20
|
+
def pattern_index
|
21
|
+
@pattern_index ||= 0
|
22
|
+
end
|
23
|
+
attr_writer :pattern_index
|
24
|
+
|
25
|
+
def initialize(output_substitute, result, *patterns)
|
26
|
+
@output_substitute = output_substitute
|
27
|
+
@result = result
|
28
|
+
@patterns = patterns
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.build(run, *patterns, result: nil)
|
32
|
+
output_substitute = run.output
|
33
|
+
|
34
|
+
patterns.map! do |pattern|
|
35
|
+
if pattern.is_a?(String)
|
36
|
+
Regexp.new("\\A#{Regexp.escape(pattern)}\\z")
|
37
|
+
else
|
38
|
+
pattern
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
new(output_substitute, result, *patterns)
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.call(run, *patterns, result: nil)
|
46
|
+
instance = build(run, *patterns, result: result)
|
47
|
+
instance.()
|
48
|
+
end
|
49
|
+
|
50
|
+
def call
|
51
|
+
output_substitute.replay_records(self)
|
52
|
+
|
53
|
+
matches
|
54
|
+
end
|
55
|
+
|
56
|
+
def enter_context(title)
|
57
|
+
push(title)
|
58
|
+
end
|
59
|
+
|
60
|
+
def exit_context(_, _)
|
61
|
+
pop
|
62
|
+
end
|
63
|
+
|
64
|
+
def finish_test(title, result)
|
65
|
+
push(title)
|
66
|
+
|
67
|
+
if match?(result)
|
68
|
+
text = stack.join("\t")
|
69
|
+
|
70
|
+
matches << text
|
71
|
+
end
|
72
|
+
|
73
|
+
pop unless title.nil?
|
74
|
+
end
|
75
|
+
|
76
|
+
def skip_test(title)
|
77
|
+
push(title)
|
78
|
+
|
79
|
+
pop
|
80
|
+
end
|
81
|
+
|
82
|
+
def push(title)
|
83
|
+
return if title.nil?
|
84
|
+
|
85
|
+
if next_pattern.match?(title)
|
86
|
+
self.pattern_index += 1
|
87
|
+
end
|
88
|
+
|
89
|
+
stack.push(title)
|
90
|
+
end
|
91
|
+
|
92
|
+
def pop
|
93
|
+
title = stack.pop
|
94
|
+
|
95
|
+
if previous_pattern.match?(title)
|
96
|
+
self.pattern_index -= 1
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def match?(result)
|
101
|
+
return false unless pattern_index == patterns.count
|
102
|
+
|
103
|
+
if self.result.nil?
|
104
|
+
true
|
105
|
+
else
|
106
|
+
result == self.result
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def next_pattern
|
111
|
+
return Pattern.none if pattern_index == patterns.count
|
112
|
+
|
113
|
+
patterns.fetch(pattern_index)
|
114
|
+
end
|
115
|
+
|
116
|
+
def previous_pattern
|
117
|
+
return Pattern.none if pattern_index.zero?
|
118
|
+
|
119
|
+
patterns[pattern_index - 1]
|
120
|
+
end
|
121
|
+
|
122
|
+
module Pattern
|
123
|
+
def self.none
|
124
|
+
%r{\z\A}
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|