test_bench-fixture 1.0.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.
@@ -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