test_bench-session 2.0.0.0 → 2.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.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/lib/test_bench/session/controls/comment.rb +93 -4
  3. data/lib/test_bench/session/controls/detail.rb +79 -4
  4. data/lib/test_bench/session/controls/event.rb +7 -0
  5. data/lib/test_bench/session/controls/events/commented.rb +91 -10
  6. data/lib/test_bench/session/controls/events/context_finished.rb +49 -11
  7. data/lib/test_bench/session/controls/events/context_skipped.rb +26 -8
  8. data/lib/test_bench/session/controls/events/context_started.rb +44 -9
  9. data/lib/test_bench/session/controls/events/detailed.rb +91 -10
  10. data/lib/test_bench/session/controls/{capture_sink/event.rb → events/event_data.rb} +2 -2
  11. data/lib/test_bench/session/controls/events/failed.rb +27 -15
  12. data/lib/test_bench/session/controls/events/fixture_finished.rb +32 -11
  13. data/lib/test_bench/session/controls/events/fixture_started.rb +26 -8
  14. data/lib/test_bench/session/controls/events/test_finished.rb +49 -11
  15. data/lib/test_bench/session/controls/events/test_skipped.rb +26 -8
  16. data/lib/test_bench/session/controls/events/test_started.rb +44 -9
  17. data/lib/test_bench/session/controls/events.rb +15 -9
  18. data/lib/test_bench/session/controls/exception.rb +28 -10
  19. data/lib/test_bench/session/controls/failure.rb +5 -16
  20. data/lib/test_bench/session/controls/fixture.rb +7 -4
  21. data/lib/test_bench/session/controls/output/detail.rb +29 -0
  22. data/lib/test_bench/session/controls/output.rb +55 -0
  23. data/lib/test_bench/session/controls/random.rb +1 -1
  24. data/lib/test_bench/session/controls/result.rb +15 -4
  25. data/lib/test_bench/session/controls/{capture_sink → substitute}/path.rb +6 -3
  26. data/lib/test_bench/session/controls/title.rb +24 -12
  27. data/lib/test_bench/session/controls.rb +12 -12
  28. data/lib/test_bench/session/events.rb +7 -14
  29. data/lib/test_bench/session/output/get.rb +27 -0
  30. data/lib/test_bench/session/output/writer/buffer/interactive/viewport.rb +164 -0
  31. data/lib/test_bench/session/output/writer/buffer/interactive.rb +139 -0
  32. data/lib/test_bench/session/output/writer/buffer.rb +27 -0
  33. data/lib/test_bench/session/output/writer/defaults.rb +17 -0
  34. data/lib/test_bench/session/output/writer/substitute.rb +17 -0
  35. data/lib/test_bench/session/output/writer.rb +95 -0
  36. data/lib/test_bench/session/output.rb +375 -0
  37. data/lib/test_bench/session/projection.rb +28 -0
  38. data/lib/test_bench/session/session.rb +51 -65
  39. data/lib/test_bench/session/store.rb +59 -0
  40. data/lib/test_bench/session/substitute/path.rb +63 -0
  41. data/lib/test_bench/session/substitute/sink.rb +104 -0
  42. data/lib/test_bench/session/substitute.rb +17 -34
  43. data/lib/test_bench/session.rb +16 -5
  44. metadata +22 -17
  45. data/lib/test_bench/session/controls/capture_sink/record.rb +0 -19
  46. data/lib/test_bench/session/controls/events/aborted.rb +0 -29
  47. data/lib/test_bench/session/controls/events/file_finished.rb +0 -32
  48. data/lib/test_bench/session/controls/events/file_started.rb +0 -29
  49. data/lib/test_bench/session/controls/events/finished.rb +0 -32
  50. data/lib/test_bench/session/controls/events/started.rb +0 -29
  51. data/lib/test_bench/session/controls/file.rb +0 -58
  52. data/lib/test_bench/session/telemetry/capture_sink/path.rb +0 -65
  53. data/lib/test_bench/session/telemetry/capture_sink/record.rb +0 -39
  54. data/lib/test_bench/session/telemetry/capture_sink.rb +0 -63
@@ -0,0 +1,375 @@
1
+ module TestBench
2
+ class Session
3
+ class Output
4
+ include TestBench::Output
5
+ include Events
6
+
7
+ def pending_writer
8
+ @pending_writer ||= Writer::Substitute.build
9
+ end
10
+ attr_writer :pending_writer
11
+
12
+ def passing_writer
13
+ @passing_writer ||= Writer::Substitute.build
14
+ end
15
+ attr_writer :passing_writer
16
+
17
+ def failing_writer
18
+ @failing_writer ||= Writer::Substitute.build
19
+ end
20
+ attr_writer :failing_writer
21
+
22
+ def failures
23
+ @failures ||= 0
24
+ end
25
+ attr_writer :failures
26
+
27
+ def mode
28
+ @mode ||= Mode.initial
29
+ end
30
+ attr_writer :mode
31
+
32
+ def branch_count
33
+ @branch_count ||= 0
34
+ end
35
+ attr_writer :branch_count
36
+
37
+ def detail_policy
38
+ @detail_policy ||= Detail.default
39
+ end
40
+ alias :detail :detail_policy
41
+ attr_writer :detail_policy
42
+
43
+ def configure(detail: nil, **arguments)
44
+ if not detail.nil?
45
+ self.detail_policy = detail
46
+ end
47
+
48
+ Writer.configure(self, **arguments, attr_name: :pending_writer)
49
+ end
50
+
51
+ def receive(event_data)
52
+ case event_data.type
53
+ when ContextStarted.event_type, TestStarted.event_type
54
+ branch
55
+ end
56
+
57
+ if initial?
58
+ handle(event_data)
59
+
60
+ else
61
+ self.mode = Mode.failing
62
+ handle(event_data)
63
+
64
+ self.mode = Mode.passing
65
+ handle(event_data)
66
+
67
+ self.mode = Mode.pending
68
+ handle(event_data)
69
+ end
70
+
71
+ case event_data.type
72
+ when ContextFinished.event_type, TestFinished.event_type
73
+ _title, result = event_data.data
74
+ merge(result)
75
+ end
76
+ end
77
+
78
+ handle ContextStarted do |context_started|
79
+ title = context_started.title
80
+
81
+ if not title.nil?
82
+ writer.
83
+ indent.
84
+ style(:green).
85
+ puts(title)
86
+
87
+ writer.indent!
88
+
89
+ if branch_count == 1
90
+ self.failures = 0
91
+ end
92
+ end
93
+ end
94
+
95
+ handle ContextFinished do |context_finished|
96
+ title = context_finished.title
97
+
98
+ if not title.nil?
99
+ writer.deindent!
100
+
101
+ if branch_count == 1
102
+ writer.puts
103
+
104
+ if failing? && failures > 0
105
+ writer.
106
+ style(:bold, :red).
107
+ puts("Failure#{'s' if not failures == 1}: #{failures}")
108
+
109
+ writer.puts
110
+ end
111
+ end
112
+ end
113
+ end
114
+
115
+ handle ContextSkipped do |context_skipped|
116
+ title = context_skipped.title
117
+
118
+ if not writer.styling?
119
+ title = "#{title} (skipped)"
120
+ end
121
+
122
+ writer.
123
+ indent.
124
+ style(:yellow).
125
+ puts(title)
126
+ end
127
+
128
+ handle TestStarted do |test_started|
129
+ title = test_started.title
130
+
131
+ if title.nil?
132
+ if passing?
133
+ return
134
+ else
135
+ title = 'Test'
136
+ end
137
+ end
138
+
139
+ writer.indent
140
+
141
+ if passing?
142
+ writer.style(:green)
143
+ elsif failing?
144
+ if not writer.styling?
145
+ title = "#{title} (failed)"
146
+ end
147
+
148
+ writer.style(:bold, :red)
149
+ elsif pending?
150
+ writer.style(:faint)
151
+ end
152
+
153
+ writer.puts(title)
154
+
155
+ writer.indent!
156
+ end
157
+
158
+ handle TestFinished do |test_finished|
159
+ title = test_finished.title
160
+
161
+ if passing? && title.nil?
162
+ return
163
+ end
164
+
165
+ writer.deindent!
166
+ end
167
+
168
+ handle TestSkipped do |test_skipped|
169
+ title = test_skipped.title
170
+
171
+ if not writer.styling?
172
+ title = "#{title} (skipped)"
173
+ end
174
+
175
+ writer.
176
+ indent.
177
+ style(:yellow).
178
+ puts(title)
179
+ end
180
+
181
+ handle Failed do |failed|
182
+ message = failed.message
183
+
184
+ if failing?
185
+ self.failures += 1
186
+ end
187
+
188
+ writer
189
+ .indent
190
+ .style(:red)
191
+ .puts(message)
192
+ end
193
+
194
+ handle Detailed do |detailed|
195
+ if not detail?
196
+ return
197
+ end
198
+
199
+ text = detailed.text
200
+ quote = detailed.quote
201
+ heading = detailed.heading
202
+
203
+ comment(text, quote, heading)
204
+ end
205
+
206
+ handle Commented do |commented|
207
+ text = commented.text
208
+ quote = commented.quote
209
+ heading = commented.heading
210
+
211
+ comment(text, quote, heading)
212
+ end
213
+
214
+ def comment(text, quote, heading)
215
+ if not heading.nil?
216
+ writer.
217
+ indent.
218
+ style(:bold, :underline).
219
+ puts(heading)
220
+
221
+ if not writer.styling?
222
+ writer.
223
+ indent.
224
+ puts('- - -')
225
+ end
226
+ end
227
+
228
+ if text.empty?
229
+ writer.
230
+ indent.
231
+ style(:faint, :italic).
232
+ puts('(empty)')
233
+ return
234
+ end
235
+
236
+ if not quote
237
+ writer.
238
+ indent.
239
+ puts(text)
240
+ else
241
+ text.each_line(chomp: true) do |line|
242
+ writer.
243
+ indent.
244
+ style(:faint).
245
+ print('> ').
246
+ style(:reset_intensity).
247
+ puts(line)
248
+ end
249
+ end
250
+ end
251
+
252
+ def current_writer
253
+ if initial? || pending?
254
+ pending_writer
255
+ elsif passing?
256
+ passing_writer
257
+ elsif failing?
258
+ failing_writer
259
+ end
260
+ end
261
+ alias :writer :current_writer
262
+
263
+ def branch
264
+ if branch_count.zero?
265
+ self.mode = Mode.pending
266
+
267
+ pending_writer.sync = false
268
+
269
+ parent_writer = pending_writer
270
+ else
271
+ parent_writer = passing_writer
272
+ end
273
+
274
+ self.branch_count += 1
275
+
276
+ self.passing_writer, self.failing_writer = parent_writer.branch
277
+ end
278
+
279
+ def merge(result)
280
+ self.branch_count -= 1
281
+
282
+ if not branched?
283
+ pending_writer.sync = true
284
+
285
+ self.mode = Mode.initial
286
+ end
287
+
288
+ if result
289
+ writer = passing_writer
290
+ else
291
+ writer = failing_writer
292
+ end
293
+
294
+ writer.flush
295
+
296
+ self.passing_writer = writer.device
297
+ self.failing_writer = writer.alternate_device
298
+ end
299
+
300
+ def branched?
301
+ branch_count > 0
302
+ end
303
+
304
+ def initial?
305
+ mode == Mode.initial
306
+ end
307
+
308
+ def pending?
309
+ mode == Mode.pending
310
+ end
311
+
312
+ def passing?
313
+ mode == Mode.passing
314
+ end
315
+
316
+ def failing?
317
+ mode == Mode.failing
318
+ end
319
+
320
+ def detail?
321
+ Detail.detail?(detail_policy, mode)
322
+ end
323
+
324
+ module Mode
325
+ def self.initial = :initial
326
+ def self.pending = :pending
327
+ def self.passing = :passing
328
+ def self.failing = :failing
329
+ end
330
+
331
+ module Detail
332
+ Error = Class.new(RuntimeError)
333
+
334
+ def self.detail?(policy, mode)
335
+ assure_detail(policy, mode)
336
+ end
337
+
338
+ def self.assure_detail(policy, mode=nil)
339
+ mode ||= Mode.initial
340
+
341
+ case policy
342
+ when on
343
+ true
344
+ when off
345
+ false
346
+ when failure
347
+ if mode == Mode.failing || mode == Mode.initial
348
+ true
349
+ else
350
+ false
351
+ end
352
+ else
353
+ raise Error, "Unknown detail policy #{policy.inspect}"
354
+ end
355
+ end
356
+
357
+ def self.on = :on
358
+ def self.off = :off
359
+ def self.failure = :failure
360
+
361
+ def self.default
362
+ policy = ENV.fetch('TEST_BENCH_DETAIL') do
363
+ return default!
364
+ end
365
+
366
+ policy.to_sym
367
+ end
368
+
369
+ def self.default!
370
+ :failure
371
+ end
372
+ end
373
+ end
374
+ end
375
+ end
@@ -0,0 +1,28 @@
1
+ module TestBench
2
+ class Session
3
+ class Projection
4
+ include Telemetry::Sink::Projection
5
+ include Events
6
+
7
+ receiver_name :session
8
+
9
+ apply Failed do
10
+ session.record_failure
11
+ end
12
+
13
+ apply TestSkipped do
14
+ session.record_skip
15
+ end
16
+
17
+ apply ContextSkipped do
18
+ session.record_skip
19
+ end
20
+
21
+ apply TestFinished do |test_finished|
22
+ if test_finished.result
23
+ session.record_assertion
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -23,77 +23,55 @@ module TestBench
23
23
  end
24
24
  attr_writer :skip_sequence
25
25
 
26
- def self.build(*sinks)
26
+ def self.build(&block)
27
27
  instance = new
28
- TestBench::Telemetry.configure(instance, *sinks)
29
- instance
30
- end
31
-
32
- def self.configure(receiver, *sinks, attr_name: nil)
33
- attr_name ||= :test_session
34
28
 
35
- instance = build(*sinks)
36
- receiver.public_send(:"#{attr_name}=", instance)
37
- end
29
+ Telemetry.configure(instance)
38
30
 
39
- def start(process_count)
40
- telemetry.record(Events::Started.new(process_count))
41
- end
42
-
43
- def abort(abort_process_id)
44
- record_failure
31
+ if not block.nil?
32
+ block.(instance.telemetry)
33
+ end
45
34
 
46
- telemetry.record(Events::Aborted.new(abort_process_id))
35
+ instance
47
36
  end
48
37
 
49
- def finish(process_count)
50
- result = !failed?
38
+ def self.configure(receiver, session: nil, attr_name: nil, &block)
39
+ session ||= Store.fetch
40
+ attr_name ||= :session
51
41
 
52
- telemetry.record(Events::Finished.new(result, process_count))
42
+ instance = session
43
+ receiver.public_send(:"#{attr_name}=", instance)
53
44
  end
54
45
 
55
- def file(path)
56
- original_failure_sequence = failure_sequence
57
-
58
- telemetry.record(Events::FileStarted.new(path))
59
-
60
- source = File.read(path)
61
-
62
- begin
63
- TOPLEVEL_BINDING.eval(source, path)
64
- rescue Failure
65
- end
66
-
67
- result = !failed?(original_failure_sequence)
68
-
69
- telemetry.record(Events::FileFinished.new(path, result))
70
-
71
- result
46
+ def passed?
47
+ asserted? && !failed? && !skipped?
72
48
  end
73
49
 
74
50
  def fixture(name, &block)
75
51
  original_failure_sequence = failure_sequence
76
52
 
77
- telemetry.record(Events::FixtureStarted.new(name))
53
+ record_event(Events::FixtureStarted.new(name))
78
54
 
79
55
  begin
80
56
  block.()
57
+
81
58
  rescue Failure
82
- end
83
59
 
84
- result = !failed?(original_failure_sequence)
60
+ ensure
61
+ result = !failed?(original_failure_sequence)
85
62
 
86
- telemetry.record(Events::FixtureFinished.new(name, result))
63
+ record_event(Events::FixtureFinished.new(name, result))
64
+ end
87
65
 
88
66
  result
89
67
  end
90
68
 
91
- def detail(text)
92
- telemetry.record(Events::Detailed.new(text))
69
+ def detail(text, quote, heading=nil)
70
+ record_event(Events::Detailed.new(text, quote, heading))
93
71
  end
94
72
 
95
- def comment(comment)
96
- telemetry.record(Events::Commented.new(comment))
73
+ def comment(text, quote, heading=nil)
74
+ record_event(Events::Commented.new(text, quote, heading))
97
75
  end
98
76
 
99
77
  def context!(...)
@@ -105,13 +83,14 @@ module TestBench
105
83
 
106
84
  def context(title=nil, &block)
107
85
  if block.nil?
108
- telemetry.record(Events::ContextSkipped.new(title))
86
+ record_skip
87
+ record_event(Events::ContextSkipped.new(title))
109
88
  return
110
89
  end
111
90
 
112
91
  original_failure_sequence = failure_sequence
113
92
 
114
- telemetry.record(Events::ContextStarted.new(title))
93
+ record_event(Events::ContextStarted.new(title))
115
94
 
116
95
  begin
117
96
  block.()
@@ -121,7 +100,7 @@ module TestBench
121
100
  ensure
122
101
  result = !failed?(original_failure_sequence)
123
102
 
124
- telemetry.record(Events::ContextFinished.new(title, result))
103
+ record_event(Events::ContextFinished.new(title, result))
125
104
  end
126
105
 
127
106
  result
@@ -134,16 +113,17 @@ module TestBench
134
113
  end
135
114
  end
136
115
 
137
- def test(path, line_number, title=nil, &block)
116
+ def test(title=nil, &block)
138
117
  if block.nil?
139
- telemetry.record(Events::TestSkipped.new(title))
118
+ record_skip
119
+ record_event(Events::TestSkipped.new(title))
140
120
  return
141
121
  end
142
122
 
143
123
  original_failure_sequence = failure_sequence
144
124
  original_assertion_sequence = assertion_sequence
145
125
 
146
- telemetry.record(Events::TestStarted.new(title))
126
+ record_event(Events::TestStarted.new(title))
147
127
 
148
128
  begin
149
129
  block.()
@@ -153,7 +133,7 @@ module TestBench
153
133
  if result
154
134
  if not asserted?(original_assertion_sequence)
155
135
  failure_message = Session.no_assertion_message
156
- fail(failure_message, path, line_number)
136
+ fail(failure_message)
157
137
  end
158
138
  end
159
139
 
@@ -161,30 +141,28 @@ module TestBench
161
141
  result = false
162
142
 
163
143
  ensure
164
- telemetry.record(Events::TestFinished.new(title, result))
144
+ record_event(Events::TestFinished.new(title, result))
165
145
  end
166
146
 
167
147
  result
168
148
  end
169
149
 
170
- def assert(result, path, line_number)
150
+ def assert(result)
171
151
  failure_message = Session.assertion_failure_message
172
152
 
173
- if result != true && result != false
174
- raise TypeError, "Value #{result.inspect} isn't a boolean"
175
- end
176
-
177
153
  record_assertion
178
154
 
179
155
  if result == false
180
- fail(failure_message, path, line_number)
156
+ fail(failure_message)
181
157
  end
182
158
  end
183
159
 
184
- def fail(message, path, line_number)
160
+ def fail(message=nil)
161
+ message ||= self.class.default_failure_message
162
+
185
163
  record_failure
186
164
 
187
- telemetry.record(Events::Failed.new(message, path, line_number))
165
+ record_event(Events::Failed.new(message))
188
166
 
189
167
  raise Failure, message
190
168
  end
@@ -205,6 +183,10 @@ module TestBench
205
183
  compare_sequence != failure_sequence
206
184
  end
207
185
 
186
+ def record_event(event)
187
+ telemetry.record(event)
188
+ end
189
+
208
190
  def record_failure
209
191
  self.failure_sequence += 1
210
192
  end
@@ -217,16 +199,20 @@ module TestBench
217
199
  self.skip_sequence += 1
218
200
  end
219
201
 
220
- def self.assertion_failure_message
221
- "Assertion failed"
202
+ def self.default_failure_message
203
+ 'Failed'
222
204
  end
223
205
 
224
- def self.no_assertion_message
225
- "Test didn't perform an assertion"
206
+ def self.assertion_failure_message
207
+ "Assertion failed"
226
208
  end
227
209
 
228
210
  def self.abort_message
229
211
  "Abort"
230
212
  end
213
+
214
+ def self.no_assertion_message
215
+ "Test didn't perform an assertion"
216
+ end
231
217
  end
232
218
  end
@@ -0,0 +1,59 @@
1
+ module TestBench
2
+ class Session
3
+ class Store
4
+ PutError = Class.new(RuntimeError)
5
+
6
+ attr_accessor :put_session
7
+
8
+ def self.configure(receiver, attr_name: nil)
9
+ attr_name ||= :session_store
10
+
11
+ receiver.public_send(:"#{attr_name}=", instance)
12
+ end
13
+
14
+ def self.instance
15
+ @instance ||= new
16
+ end
17
+
18
+ def self.put(...) = instance.put(...)
19
+ def self.put?(...) = instance.put?(...)
20
+ def self.reset(...) = instance.reset(...)
21
+ def self.get(...) = instance.get(...)
22
+ def self.fetch(...) = instance.fetch(...)
23
+
24
+ def put(session)
25
+ if not put_session.nil?
26
+ raise PutError, "Already put a session"
27
+ end
28
+
29
+ self.put_session = session
30
+ end
31
+
32
+ def put?(session=nil)
33
+ if not session.nil?
34
+ put_session == session
35
+ else
36
+ !put_session.nil?
37
+ end
38
+ end
39
+
40
+ def reset(session=nil)
41
+ self.put_session = session
42
+ end
43
+
44
+ def get
45
+ put_session
46
+ end
47
+
48
+ def fetch
49
+ self.put_session ||= new_session
50
+ end
51
+
52
+ def new_session
53
+ Session.build do |telemetry|
54
+ Output.register_telemetry(telemetry)
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end