test_bench-fixture 1.0.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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