test_bench-output 2.1.1.2 → 3.0.0.0.pre.1

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 (41) hide show
  1. checksums.yaml +4 -4
  2. data/lib/test_bench/output/comment_style.rb +61 -0
  3. data/lib/test_bench/output/controls/comment_style.rb +21 -0
  4. data/lib/test_bench/output/controls/event.rb +2 -2
  5. data/lib/test_bench/output/controls/events/aborted.rb +9 -0
  6. data/lib/test_bench/output/controls/events/commented.rb +21 -0
  7. data/lib/test_bench/output/controls/events/context_finished.rb +9 -0
  8. data/lib/test_bench/output/controls/events/context_started.rb +9 -0
  9. data/lib/test_bench/output/controls/events/detailed.rb +21 -0
  10. data/lib/test_bench/output/controls/events/failed.rb +9 -0
  11. data/lib/test_bench/output/controls/events/file_executed.rb +9 -0
  12. data/lib/test_bench/output/controls/events/file_not_found.rb +9 -0
  13. data/lib/test_bench/output/controls/events/file_queued.rb +9 -0
  14. data/lib/test_bench/output/controls/events/skipped.rb +9 -0
  15. data/lib/test_bench/output/controls/events/test_finished.rb +9 -0
  16. data/lib/test_bench/output/controls/events/test_started.rb +9 -0
  17. data/lib/test_bench/output/controls/random.rb +2 -2
  18. data/lib/test_bench/output/controls/session.rb +17 -0
  19. data/lib/test_bench/output/controls/status.rb +7 -0
  20. data/lib/test_bench/output/controls/style.rb +14 -6
  21. data/lib/test_bench/output/controls/text.rb +16 -4
  22. data/lib/test_bench/output/controls.rb +21 -5
  23. data/lib/test_bench/output/detail_policy.rb +44 -0
  24. data/lib/test_bench/output/device/null.rb +3 -3
  25. data/lib/test_bench/output/device/substitute.rb +19 -33
  26. data/lib/test_bench/output/device.rb +73 -0
  27. data/lib/test_bench/output/get.rb +30 -0
  28. data/lib/test_bench/output/level.rb +51 -0
  29. data/lib/test_bench/output/output.rb +540 -36
  30. data/lib/test_bench/output/writer/style.rb +5 -3
  31. data/lib/test_bench/output/writer/substitute.rb +10 -23
  32. data/lib/test_bench/output/writer.rb +54 -147
  33. data/lib/test_bench/output.rb +10 -4
  34. metadata +49 -21
  35. data/lib/test_bench/output/controls/data.rb +0 -49
  36. data/lib/test_bench/output/controls/device.rb +0 -27
  37. data/lib/test_bench/output/controls/output.rb +0 -34
  38. data/lib/test_bench/output/controls/styling.rb +0 -27
  39. data/lib/test_bench/output/digest.rb +0 -113
  40. data/lib/test_bench/output/writer/buffer.rb +0 -57
  41. data/lib/test_bench/output/writer/defaults.rb +0 -11
@@ -0,0 +1,51 @@
1
+ module TestBench
2
+ class Output
3
+ module Level
4
+ Error = Class.new(RuntimeError)
5
+
6
+ def self.abort
7
+ :abort
8
+ end
9
+
10
+ def self.failure
11
+ :failure
12
+ end
13
+
14
+ def self.not_passing
15
+ :not_passing
16
+ end
17
+
18
+ def self.all
19
+ :all
20
+ end
21
+
22
+ def self.output?(output_level, result=nil)
23
+ result ||= Session::Result.none
24
+
25
+ case output_level
26
+ when abort
27
+ result == Session::Result.aborted
28
+
29
+ when failure
30
+ [
31
+ Session::Result.aborted,
32
+ Session::Result.failed
33
+ ].include?(result)
34
+
35
+ when not_passing
36
+ result != Session::Result.passed
37
+
38
+ when all
39
+ true
40
+
41
+ else
42
+ raise Error, "Incorrect output level: #{output_level.inspect}"
43
+ end
44
+ end
45
+
46
+ def self.assure_output_level(output_level)
47
+ output?(output_level)
48
+ end
49
+ end
50
+ end
51
+ end
@@ -1,66 +1,570 @@
1
1
  module TestBench
2
- module Output
3
- def self.included(cls)
4
- cls.class_exec do
5
- include Telemetry::Sink::Handler
2
+ class Output
3
+ include Telemetry::Sink::Handler
4
+ include ImportConstants
6
5
 
7
- extend Build
8
- extend RegisterTelemetry
9
- extend Configure
10
- end
11
- end
6
+ import_constants Session::Events
7
+
8
+ Result = Session::Result
12
9
 
13
10
  def writer
14
11
  @writer ||= Writer::Substitute.build
15
12
  end
16
13
  attr_writer :writer
17
14
 
18
- def configure(writer: nil, device: nil, styling: nil)
19
- Writer.configure(self, writer:, device:, styling:)
15
+ def detail_policy
16
+ @detail_policy ||= DetailPolicy.on
17
+ end
18
+ attr_writer :detail_policy
19
+
20
+ def output_level
21
+ @output_level ||= Level.all
22
+ end
23
+ attr_writer :output_level
24
+
25
+ def branches
26
+ @branches ||= []
27
+ end
28
+ attr_writer :branches
29
+
30
+ def status
31
+ @status ||= Session::Status.initial
32
+ end
33
+ attr_writer :status
34
+
35
+ def self.build(styling: nil, device: nil)
36
+ instance = new
37
+
38
+ instance.detail_policy = Defaults.detail_policy
39
+ instance.output_level = Defaults.output_level
40
+
41
+ Writer.configure(instance, styling:, device:)
42
+
43
+ instance
44
+ end
45
+
46
+ def self.register_telemetry_sink(session=nil, styling: nil, device: nil)
47
+ session ||= Session.instance
48
+
49
+ output = build(styling:, device:)
50
+
51
+ session.register_telemetry_sink(output)
52
+
53
+ output
54
+ end
55
+
56
+ handle Failed do |failed|
57
+ pend(failed)
58
+ end
59
+
60
+ handle Aborted do |aborted|
61
+ pend(aborted)
62
+ end
63
+
64
+ handle Skipped do |skipped|
65
+ pend(skipped)
66
+ end
67
+
68
+ handle Commented do |commented|
69
+ pend(commented)
70
+ end
71
+
72
+ handle Detailed do |detailed|
73
+ pend(detailed)
74
+ end
75
+
76
+ handle TestStarted do |test_started|
77
+ branch(test_started)
78
+ end
79
+
80
+ handle TestFinished do |test_finished|
81
+ merge(test_finished)
82
+ end
83
+
84
+ handle ContextStarted do |context_started|
85
+ branch(context_started)
86
+ end
87
+
88
+ handle ContextFinished do |context_finished|
89
+ merge(context_finished)
90
+ end
91
+
92
+ handle FileQueued do |file_queued|
93
+ branch(file_queued)
94
+ end
95
+
96
+ handle FileExecuted do |file_executed|
97
+ merge(file_executed)
98
+ end
99
+
100
+ handle FileNotFound do |file_not_found|
101
+ pend(file_not_found)
102
+ end
103
+
104
+ def merge(event)
105
+ pend(event)
106
+
107
+ merge_branch = branches.pop
108
+
109
+ if root?
110
+ if not Level.output?(output_level, merge_branch.status.result)
111
+ return
112
+ end
113
+
114
+ merge_branch.each do |event, status|
115
+ resolve(event, status)
116
+ end
117
+ else
118
+ current_branch.merge(merge_branch)
119
+ end
120
+ end
121
+
122
+ def branch(event)
123
+ branch = Branch.new
124
+
125
+ branches << branch
126
+
127
+ pend(event)
128
+
129
+ branch
20
130
  end
21
131
 
22
- module Build
23
- def build(writer: nil, device: nil, styling: nil, **arguments)
24
- instance = new
25
- instance.configure(writer:, device:, styling:, **arguments)
26
- instance
132
+ def pend(event)
133
+ status.update(event)
134
+
135
+ if root?
136
+ resolve(event, status)
137
+ else
138
+ current_branch.pend(event)
27
139
  end
28
140
  end
29
141
 
30
- module RegisterTelemetry
31
- def register_telemetry(telemetry, **arguments)
32
- instance = build(**arguments)
33
- telemetry.register(instance)
34
- instance
142
+ def current_branch
143
+ branches.last
144
+ end
145
+
146
+ def root?
147
+ current_branch.nil?
148
+ end
149
+
150
+ def resolve(event, status)
151
+ case event
152
+ in Failed => failed
153
+ resolve_failed(failed)
154
+ in Aborted => aborted
155
+ resolve_aborted(aborted)
156
+ in Skipped => skipped
157
+ resolve_skipped(skipped)
158
+ in Commented => commented
159
+ resolve_commented(commented)
160
+ in Detailed => detailed
161
+ resolve_detailed(detailed, status)
162
+ in TestStarted => test_started
163
+ resolve_test_started(test_started, status)
164
+ in TestFinished => test_finished
165
+ resolve_test_finished(test_finished)
166
+ in ContextStarted => context_started
167
+ resolve_context_started(context_started, status)
168
+ in ContextFinished => context_finished
169
+ resolve_context_finished(context_finished, status)
170
+ in FileQueued => file_queued
171
+ resolve_file_queued(file_queued, status)
172
+ in FileExecuted => file_executed
173
+ resolve_file_executed(file_executed)
174
+ in FileNotFound => file_not_found
175
+ resolve_file_not_found(file_not_found)
176
+ else
35
177
  end
36
- alias :register :register_telemetry
37
178
  end
38
179
 
39
- module Configure
40
- def configure(receiver, attr_name: nil, **arguments)
41
- attr_name ||= :output
180
+ def resolve_failed(failed)
181
+ message = failed.message
42
182
 
43
- instance = build(**arguments)
44
- receiver.public_send(:"#{attr_name}=", instance)
183
+ writer.
184
+ indent.
185
+ style(:red).
186
+ puts(message)
187
+ end
188
+
189
+ def resolve_aborted(aborted)
190
+ message = aborted.message
191
+
192
+ writer.
193
+ indent.
194
+ style(:bold, :red).
195
+ print("Aborted: ").
196
+ style(:reset_intensity).
197
+ puts(message)
198
+ end
199
+
200
+ def resolve_skipped(skipped)
201
+ message = skipped.message
202
+
203
+ writer.
204
+ indent.
205
+ style(:yellow)
206
+
207
+ if not message.nil?
208
+ writer.print(message)
209
+
210
+ if not writer.styling?
211
+ writer.print(" (skipped)")
212
+ end
213
+ else
214
+ writer.print("Skipped")
45
215
  end
216
+
217
+ writer.puts
46
218
  end
47
219
 
48
- module Substitute
49
- def self.build
50
- Output.new
220
+ def resolve_commented(commented)
221
+ text = commented.text
222
+
223
+ disposition = commented.disposition
224
+ style = CommentStyle.fetch(disposition)
225
+
226
+ case style
227
+ when CommentStyle.detect
228
+ if text.end_with?("\n")
229
+ style = CommentStyle.block
230
+ else
231
+ style = CommentStyle.normal
232
+ end
233
+
234
+ when CommentStyle.raw
235
+ writer.
236
+ print(text).
237
+ print("\n")
238
+
239
+ return
240
+ end
241
+
242
+ if text.empty?
243
+ writer.
244
+ indent.
245
+ style(:faint, :italic)
246
+
247
+ if style == CommentStyle.heading
248
+ writer.puts('(no heading)')
249
+
250
+ if not writer.styling?
251
+ writer.
252
+ indent.
253
+ puts("- - -")
254
+ end
255
+ else
256
+ writer.puts('(empty)')
257
+ end
258
+
259
+ return
51
260
  end
52
261
 
53
- class Output < Telemetry::Substitute::Sink
54
- def handle(event_or_event_data)
55
- if event_or_event_data.is_a?(Telemetry::Event)
56
- event_data = Telemetry::Event::Export.(event_or_event_data)
262
+ case style
263
+ when CommentStyle.normal
264
+ writer.
265
+ indent.
266
+ puts(text)
267
+
268
+ when CommentStyle.heading
269
+ writer.
270
+ indent.
271
+ style(:bold).
272
+ puts(text)
273
+
274
+ if not writer.styling?
275
+ writer.
276
+ indent.
277
+ puts("- - -")
278
+ end
279
+
280
+ when CommentStyle.block
281
+ text.each_line do |line|
282
+ writer.
283
+ indent.
284
+ style(:reverse_video)
285
+
286
+ if writer.styling?
287
+ writer.print(' ')
57
288
  else
58
- event_data = event_or_event_data
289
+ writer.print('>')
59
290
  end
60
291
 
61
- receive(event_data)
292
+ writer.
293
+ style(:reset_reverse_video).
294
+ print(' ').
295
+ puts(line)
296
+ end
297
+
298
+ when CommentStyle.line_number
299
+ lines = text.each_line
300
+
301
+ final_line_number_width = lines.count.to_s.length
302
+ marker_width = final_line_number_width + 2
303
+
304
+ lines.with_index(1) do |line, line_number|
305
+ line_marker = "#{line_number}.".ljust(marker_width)
306
+
307
+ writer.
308
+ indent.
309
+ style(:faint).
310
+ print(line_marker).
311
+ style(:reset_intensity).
312
+ puts(line)
62
313
  end
63
314
  end
64
315
  end
316
+
317
+ def resolve_detailed(detailed, status)
318
+ result = status.result
319
+
320
+ print_detail = DetailPolicy.detail?(detail_policy, result)
321
+
322
+ if print_detail
323
+ resolve_commented(detailed)
324
+ end
325
+ end
326
+
327
+ def resolve_test_started(test_started, status)
328
+ title = test_started.title
329
+
330
+ if title.nil?
331
+ return
332
+ end
333
+
334
+ writer.indent
335
+
336
+ result = status.result
337
+
338
+ case result
339
+ when Result.passed
340
+ writer.style(:green)
341
+ when Result.failed, Result.aborted, Result.incomplete, Result.none
342
+ writer.style(:bold, :red)
343
+ end
344
+
345
+ writer.print(title)
346
+
347
+ if not writer.styling?
348
+ case result
349
+ when Result.aborted
350
+ writer.print(" (aborted)")
351
+ when Result.failed
352
+ writer.print(" (failed)")
353
+ when Result.incomplete, Result.none
354
+ writer.print(" (inconclusive)")
355
+ end
356
+ end
357
+
358
+ writer.puts
359
+
360
+ writer.increase_indentation
361
+ end
362
+
363
+ def resolve_test_finished(test_finished)
364
+ title = test_finished.title
365
+
366
+ if not title.nil?
367
+ writer.decrease_indentation
368
+ end
369
+ end
370
+
371
+ def resolve_context_started(context_started, status)
372
+ title = context_started.title
373
+
374
+ if title.nil?
375
+ return
376
+ end
377
+
378
+ writer.indent
379
+
380
+ result = status.result
381
+
382
+ case result
383
+ when Result.aborted
384
+ writer.style(:red)
385
+ when Result.passed, Result.failed, Result.incomplete
386
+ writer.style(:green)
387
+ end
388
+
389
+ writer.print(title)
390
+
391
+ if not writer.styling?
392
+ if result == Result.aborted
393
+ writer.print(" (aborted)")
394
+ end
395
+ end
396
+
397
+ writer.puts
398
+
399
+ writer.increase_indentation
400
+ end
401
+
402
+ def resolve_context_finished(context_finished, status)
403
+ title = context_finished.title
404
+
405
+ if title.nil?
406
+ return
407
+ end
408
+
409
+ writer.decrease_indentation
410
+
411
+ if not writer.indentation_depth.zero?
412
+ return
413
+ end
414
+
415
+ error_count = status.error_sequence
416
+ failure_count = status.failure_sequence
417
+ skip_count = status.skip_sequence
418
+
419
+ if error_count > 0 || failure_count > 0 || skip_count > 0
420
+ writer.puts
421
+ end
422
+
423
+ if not error_count.zero?
424
+ writer.
425
+ style(:red).
426
+ print("Errors: ").
427
+ style(:bold).
428
+ puts("#{status.error_sequence}")
429
+ end
430
+
431
+ if not failure_count.zero?
432
+ writer.
433
+ style(:red).
434
+ print("Failures: ").
435
+ style(:bold).
436
+ puts("#{status.failure_sequence}")
437
+ end
438
+
439
+ if not skip_count.zero?
440
+ writer.
441
+ style(:yellow).
442
+ print("Skipped: ").
443
+ style(:bold).
444
+ puts("#{status.skip_sequence}")
445
+ end
446
+ end
447
+
448
+ def resolve_file_queued(file_queued, status)
449
+ writer.indent
450
+
451
+ if status.result == Session::Result.aborted
452
+ writer.style(:bold, :red)
453
+ end
454
+
455
+ file = file_queued.file
456
+ writer.print("Running #{file}")
457
+
458
+ if !writer.styling? && status.result == Session::Result.aborted
459
+ writer.print(" (aborted)")
460
+ end
461
+
462
+ writer.puts
463
+ end
464
+
465
+ def resolve_file_executed(file_executed)
466
+ indented = writer.indentation_depth > 0
467
+
468
+ result = file_executed.result
469
+
470
+ if indented
471
+ file = file_executed.file
472
+
473
+ writer.
474
+ indent.
475
+ style(:italic).
476
+ print("Ran #{file}")
477
+
478
+ if result == Result.none
479
+ writer.
480
+ print(' ').
481
+ style(:faint).
482
+ print("(no tests)")
483
+ end
484
+
485
+ writer.puts
486
+ else
487
+ if result == Result.none
488
+ writer.
489
+ style(:faint, :italic).
490
+ puts("(no tests)")
491
+ end
492
+
493
+ writer.puts
494
+ end
495
+ end
496
+
497
+ def resolve_file_not_found(file_not_found)
498
+ file = file_not_found.file
499
+
500
+ writer.
501
+ style(:red).
502
+ print("File not found: ").
503
+ style(:bold).
504
+ puts(file).
505
+ puts
506
+ end
507
+
508
+ class Branch
509
+ def status
510
+ @status ||= Session::Status.initial
511
+ end
512
+ attr_writer :status
513
+
514
+ def entries
515
+ @entries ||= []
516
+ end
517
+ attr_writer :entries
518
+
519
+ def pend(event)
520
+ status.update(event)
521
+
522
+ entries << event
523
+ end
524
+
525
+ def merge(branch)
526
+ branch_status = branch.status
527
+
528
+ status.test_sequence += branch_status.test_sequence
529
+ status.failure_sequence += branch_status.failure_sequence
530
+ status.error_sequence += branch_status.error_sequence
531
+ status.skip_sequence += branch_status.skip_sequence
532
+
533
+ entries << branch
534
+ end
535
+
536
+ def each(&block)
537
+ entries.each do |entry|
538
+ case entry
539
+ in Telemetry::Event => event
540
+ block.(event, status)
541
+ in Branch => branch
542
+ branch.each(&block)
543
+ end
544
+ end
545
+ end
546
+ end
547
+
548
+ module Defaults
549
+ def self.detail_policy
550
+ env_var_value = ENV.fetch('TEST_BENCH_OUTPUT_DETAIL', 'failure')
551
+
552
+ detail_policy = env_var_value.to_sym
553
+
554
+ DetailPolicy.assure_policy(detail_policy)
555
+
556
+ detail_policy
557
+ end
558
+
559
+ def self.output_level
560
+ env_var_value = ENV.fetch('TEST_BENCH_OUTPUT_LEVEL', 'all')
561
+
562
+ output_level = env_var_value.gsub('-', '_').to_sym
563
+
564
+ Level.assure_output_level(output_level)
565
+
566
+ output_level
567
+ end
568
+ end
65
569
  end
66
570
  end
@@ -1,27 +1,29 @@
1
1
  module TestBench
2
- module Output
2
+ class Output
3
3
  class Writer
4
4
  module Style
5
5
  Error = Class.new(RuntimeError)
6
6
 
7
7
  def self.control_code(style)
8
8
  control_codes.fetch(style) do
9
- raise Error, "Invalid style #{style.inspect}"
9
+ raise Error, "Incorrect style: #{style.inspect}"
10
10
  end
11
11
  end
12
12
 
13
13
  def self.control_codes
14
14
  @sgr_codes ||= {
15
- :reset => '0',
15
+ :reset => '',
16
16
 
17
17
  :bold => '1',
18
18
  :faint => '2',
19
19
  :italic => '3',
20
20
  :underline => '4',
21
+ :reverse_video => '7',
21
22
 
22
23
  :reset_intensity => '22',
23
24
  :reset_italic => '23',
24
25
  :reset_underline => '24',
26
+ :reset_reverse_video => '27',
25
27
 
26
28
  :black => '30',
27
29
  :red => '31',