test_bench 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.
Files changed (48) hide show
  1. checksums.yaml +7 -0
  2. data/lib/test_bench.rb +37 -0
  3. data/lib/test_bench/cli.rb +43 -0
  4. data/lib/test_bench/cli/parse_arguments.rb +168 -0
  5. data/lib/test_bench/controls.rb +22 -0
  6. data/lib/test_bench/controls/caller_location.rb +5 -0
  7. data/lib/test_bench/controls/depth.rb +21 -0
  8. data/lib/test_bench/controls/device.rb +27 -0
  9. data/lib/test_bench/controls/directory.rb +15 -0
  10. data/lib/test_bench/controls/error.rb +35 -0
  11. data/lib/test_bench/controls/fixture.rb +29 -0
  12. data/lib/test_bench/controls/output/escape_code.rb +23 -0
  13. data/lib/test_bench/controls/output/newline_character.rb +11 -0
  14. data/lib/test_bench/controls/output/print_error.rb +19 -0
  15. data/lib/test_bench/controls/output/styling.rb +29 -0
  16. data/lib/test_bench/controls/output/summary/error.rb +44 -0
  17. data/lib/test_bench/controls/output/summary/run.rb +59 -0
  18. data/lib/test_bench/controls/path.rb +15 -0
  19. data/lib/test_bench/controls/pattern.rb +29 -0
  20. data/lib/test_bench/controls/result.rb +5 -0
  21. data/lib/test_bench/controls/test_file.rb +5 -0
  22. data/lib/test_bench/controls/time.rb +61 -0
  23. data/lib/test_bench/deactivation_variants.rb +21 -0
  24. data/lib/test_bench/environment/boolean.rb +40 -0
  25. data/lib/test_bench/fixtures.rb +3 -0
  26. data/lib/test_bench/fixtures/configure_receiver.rb +80 -0
  27. data/lib/test_bench/output.rb +9 -0
  28. data/lib/test_bench/output/build.rb +57 -0
  29. data/lib/test_bench/output/levels/debug.rb +229 -0
  30. data/lib/test_bench/output/levels/failure.rb +31 -0
  31. data/lib/test_bench/output/levels/none.rb +13 -0
  32. data/lib/test_bench/output/levels/pass.rb +274 -0
  33. data/lib/test_bench/output/levels/summary.rb +21 -0
  34. data/lib/test_bench/output/print_error.rb +163 -0
  35. data/lib/test_bench/output/summary/error.rb +77 -0
  36. data/lib/test_bench/output/summary/run.rb +102 -0
  37. data/lib/test_bench/output/summary/run/print.rb +98 -0
  38. data/lib/test_bench/output/timer.rb +75 -0
  39. data/lib/test_bench/output/timer/substitute.rb +45 -0
  40. data/lib/test_bench/output/writer.rb +192 -0
  41. data/lib/test_bench/output/writer/dependency.rb +12 -0
  42. data/lib/test_bench/output/writer/sgr.rb +54 -0
  43. data/lib/test_bench/output/writer/substitute.rb +38 -0
  44. data/lib/test_bench/run.rb +103 -0
  45. data/lib/test_bench/run/substitute.rb +36 -0
  46. data/lib/test_bench/test_bench.rb +76 -0
  47. data/script/bench +19 -0
  48. metadata +117 -0
@@ -0,0 +1,21 @@
1
+ module TestBench
2
+ module Output
3
+ module Levels
4
+ class Summary
5
+ include TestBench::Fixture::Output
6
+
7
+ include Output::Summary::Error
8
+ include Output::Summary::Run
9
+
10
+ def self.build(writer: nil, styling: nil, device: nil)
11
+ warn "Warning: #{self} is deprecated. It will remain in the TestBench v2 series, but will be removed from TestBench v3"
12
+
13
+ instance = new
14
+ Writer.configure(instance, writer: writer, styling: styling, device: device)
15
+ Timer.configure(instance)
16
+ instance
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,163 @@
1
+ module TestBench
2
+ module Output
3
+ module PrintError
4
+ include Writer::Dependency
5
+
6
+ def omit_backtrace_pattern
7
+ @omit_backtrace_pattern ||= Defaults.omit_backtrace_pattern
8
+ end
9
+ attr_writer :omit_backtrace_pattern
10
+
11
+ def reverse_backtraces
12
+ instance_variable_defined?(:@reverse_backtraces) ?
13
+ @reverse_backtraces :
14
+ @reverse_backtraces = Defaults.reverse_backtraces
15
+ end
16
+ attr_writer :reverse_backtraces
17
+
18
+ def print_error(error)
19
+ PrintError.(error, writer: writer, omit_backtrace_pattern: omit_backtrace_pattern, reverse_backtraces: reverse_backtraces)
20
+ end
21
+
22
+ def self.call(error, writer: nil, omit_backtrace_pattern: nil, reverse_backtraces: nil)
23
+ writer ||= Writer.build
24
+ omit_backtrace_pattern ||= Defaults.omit_backtrace_pattern
25
+ reverse_backtraces = Defaults.reverse_backtraces if reverse_backtraces.nil?
26
+
27
+ writer.escape_code(:red)
28
+
29
+ if reverse_backtraces && error.backtrace.length > 1
30
+ writer
31
+ .indent
32
+ .escape_code(:bold)
33
+ .text("Traceback")
34
+ .escape_code(:reset_intensity)
35
+ .text(" (most recent call last):")
36
+ .newline
37
+ end
38
+
39
+ error(error, writer: writer, omit_backtrace_pattern: omit_backtrace_pattern, reverse_backtraces: reverse_backtraces)
40
+
41
+ writer
42
+ .escape_code(:reset_fg)
43
+ .sync
44
+ end
45
+
46
+ def self.error(error, writer: nil, omit_backtrace_pattern: nil, reverse_backtraces: nil)
47
+ reverse_backtraces = self.reverse_backtraces if reverse_backtraces.nil?
48
+
49
+ if reverse_backtraces
50
+ error_cause(error, writer: writer, omit_backtrace_pattern: omit_backtrace_pattern, reverse_backtraces: reverse_backtraces)
51
+ error_backtrace(error, writer: writer, omit_backtrace_pattern: omit_backtrace_pattern, reverse_backtraces: reverse_backtraces)
52
+ error_message(error, writer: writer)
53
+ else
54
+ error_message(error, writer: writer)
55
+ error_backtrace(error, writer: writer, omit_backtrace_pattern: omit_backtrace_pattern, reverse_backtraces: reverse_backtraces)
56
+ error_cause(error, writer: writer, omit_backtrace_pattern: omit_backtrace_pattern, reverse_backtraces: reverse_backtraces)
57
+ end
58
+ end
59
+
60
+ def self.error_cause(error, **args)
61
+ error(error.cause, **args) unless error.cause.nil?
62
+ end
63
+
64
+ def self.error_message(error, writer: nil)
65
+ caller_location = error.backtrace[0]
66
+
67
+ message = error.message
68
+
69
+ error_class = error.class.name
70
+
71
+ writer
72
+ .indent
73
+ .text("#{caller_location}: ")
74
+ .escape_code(:bold)
75
+ .text("#{message} (")
76
+ .escape_code(:underline)
77
+ .text(error_class)
78
+ .escape_code(:reset_underline)
79
+ .text(")")
80
+ .escape_code(:reset_intensity)
81
+ .newline
82
+ end
83
+
84
+ def self.error_backtrace(error, writer: nil, omit_backtrace_pattern: nil, reverse_backtraces: nil)
85
+ writer ||= self.writer
86
+ omit_backtrace_pattern ||= self.omit_backtrace_pattern
87
+ reverse_backtraces = self.reverse_backtraces if reverse_backtraces.nil?
88
+
89
+ omitting = false
90
+
91
+ backtrace = error.backtrace[1..-1]
92
+
93
+ unless reverse_backtraces
94
+ backtrace_iterator = backtrace.each.with_index
95
+ else
96
+ frame_count = backtrace.count
97
+
98
+ number_width = frame_count.to_s.each_char.count
99
+
100
+ backtrace_iterator = backtrace.reverse_each.map.with_index do |location, index|
101
+ ordinal = frame_count - index
102
+
103
+ ordinal = ordinal.to_s.rjust(number_width, ' ')
104
+
105
+ [location, ordinal]
106
+ end
107
+ end
108
+
109
+ backtrace_iterator.each do |location, ordinal|
110
+ omit = omit_backtrace_pattern.match?(location)
111
+
112
+ next if omit && omitting
113
+
114
+ writer
115
+ .text("\t")
116
+ .indent
117
+
118
+ if omit
119
+ omitting = true
120
+
121
+ if reverse_backtraces
122
+ ordinal.gsub!(/[[:digit:]]/, '?')
123
+
124
+ writer.text("#{ordinal}: ")
125
+ end
126
+
127
+ writer
128
+ .escape_code(:faint)
129
+ .escape_code(:italic)
130
+ .text('*omitted*')
131
+ .escape_code(:reset_italic)
132
+ .escape_code(:reset_intensity)
133
+
134
+ else
135
+ omitting = false
136
+
137
+ if reverse_backtraces
138
+ writer.text("#{ordinal}: ")
139
+ end
140
+
141
+ writer.text("from #{location}")
142
+ end
143
+
144
+ writer.newline
145
+ end
146
+ end
147
+
148
+ module Defaults
149
+ def self.omit_backtrace_pattern
150
+ pattern = ENV.fetch('TEST_BENCH_OMIT_BACKTRACE_PATTERN') do
151
+ 'test_bench'
152
+ end
153
+
154
+ Regexp.new(pattern)
155
+ end
156
+
157
+ def self.reverse_backtraces
158
+ Environment::Boolean.fetch('TEST_BENCH_REVERSE_BACKTRACES', false)
159
+ end
160
+ end
161
+ end
162
+ end
163
+ end
@@ -0,0 +1,77 @@
1
+ module TestBench
2
+ module Output
3
+ module Summary
4
+ module Error
5
+ include Writer::Dependency
6
+
7
+ def self.included(cls)
8
+ cls.prepend(OutputMethods)
9
+ end
10
+
11
+ def error_summary_errors
12
+ @error_summary_errors ||= Hash.new do |hash, key|
13
+ hash[key] = []
14
+ end
15
+ end
16
+ attr_writer :error_summary_errors
17
+
18
+ attr_accessor :error_summary_current_file
19
+
20
+ def error_summary
21
+ return if error_summary_errors.empty?
22
+
23
+ writer
24
+ .escape_code(:bold)
25
+ .escape_code(:red)
26
+ .text('Error Summary:')
27
+ .escape_code(:reset_intensity)
28
+ .escape_code(:reset_fg)
29
+ .newline
30
+
31
+ error_summary_errors.each do |file, errors|
32
+ error_count = errors.count
33
+
34
+ writer
35
+ .text(error_count.to_s.rjust(4, ' '))
36
+ .text(": #{file}")
37
+ .newline
38
+
39
+ errors.each do |error|
40
+ writer
41
+ .text(' ')
42
+ .escape_code(:red)
43
+
44
+ PrintError.error_message(error, writer: writer)
45
+
46
+ writer.escape_code(:reset_fg)
47
+ end
48
+ end
49
+
50
+ writer.newline
51
+ end
52
+
53
+ module OutputMethods
54
+ def enter_file(file)
55
+ super
56
+
57
+ self.error_summary_current_file = file
58
+ end
59
+
60
+ def error(error)
61
+ super
62
+
63
+ current_file = error_summary_current_file
64
+
65
+ self.error_summary_errors[current_file] << error
66
+ end
67
+
68
+ def finish(_)
69
+ super
70
+
71
+ error_summary
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,102 @@
1
+ module TestBench
2
+ module Output
3
+ module Summary
4
+ module Run
5
+ include Writer::Dependency
6
+ extend Print
7
+
8
+ def self.included(cls)
9
+ cls.prepend(OutputMethods)
10
+ end
11
+
12
+ def file_count
13
+ @file_count ||= 0
14
+ end
15
+ attr_writer :file_count
16
+
17
+ def test_count
18
+ @test_count ||= 0
19
+ end
20
+ attr_writer :test_count
21
+
22
+ def pass_count
23
+ @pass_count ||= 0
24
+ end
25
+ attr_writer :pass_count
26
+
27
+ def skip_count
28
+ @skip_count ||= 0
29
+ end
30
+ attr_writer :skip_count
31
+
32
+ def failure_count
33
+ @failure_count ||= 0
34
+ end
35
+ attr_writer :failure_count
36
+
37
+ def error_count
38
+ @error_count ||= 0
39
+ end
40
+ attr_writer :error_count
41
+
42
+ def elapsed_time
43
+ @elapsed_time ||= 0
44
+ end
45
+ attr_writer :elapsed_time
46
+
47
+ def timer
48
+ @timer ||= Timer::Substitute.build
49
+ end
50
+ attr_writer :timer
51
+
52
+ module OutputMethods
53
+ def enter_file(_)
54
+ super
55
+
56
+ timer.start
57
+ end
58
+
59
+ def exit_file(_, _)
60
+ super
61
+
62
+ elapsed_time = timer.stop
63
+
64
+ self.elapsed_time += elapsed_time
65
+
66
+ self.file_count += 1
67
+ end
68
+
69
+ def finish_test(_, result)
70
+ super
71
+
72
+ self.test_count += 1
73
+
74
+ if result
75
+ self.pass_count += 1
76
+ else
77
+ self.failure_count += 1
78
+ end
79
+ end
80
+
81
+ def skip_test(_)
82
+ super
83
+
84
+ self.skip_count += 1
85
+ end
86
+
87
+ def error(_)
88
+ super
89
+
90
+ self.error_count += 1
91
+ end
92
+
93
+ def finish(_)
94
+ super
95
+
96
+ Print.(file_count: file_count, test_count: test_count, pass_count: pass_count, skip_count: skip_count, failure_count: failure_count, error_count: error_count, elapsed_time: elapsed_time, writer: writer)
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,98 @@
1
+ module TestBench
2
+ module Output
3
+ module Summary
4
+ module Run
5
+ module Print
6
+ extend self
7
+
8
+ def call(file_count: nil, test_count: nil, pass_count: nil, skip_count: nil, failure_count: nil, error_count: nil, elapsed_time: nil, writer: nil)
9
+ file_count ||= 0
10
+ test_count ||= 0
11
+ pass_count ||= 0
12
+ skip_count ||= 0
13
+ failure_count ||= 0
14
+ error_count ||= 0
15
+ elapsed_time ||= 0
16
+ writer ||= Writer.build
17
+
18
+ failed = error_count > 0
19
+
20
+ if elapsed_time.nonzero?
21
+ tests_per_second = test_count / elapsed_time
22
+ end
23
+
24
+ if failed
25
+ writer.escape_code(:red)
26
+ end
27
+
28
+ writer
29
+ .text("Finished running #{numeric_label(file_count, 'file')}")
30
+ .newline
31
+ .text("Ran %s in %.3fs (%.1f tests/second)" % [
32
+ numeric_label(test_count, 'test'),
33
+ elapsed_time,
34
+ tests_per_second || 0])
35
+ .newline
36
+
37
+ if pass_count.nonzero? && !failed
38
+ writer
39
+ .escape_code(:green)
40
+ .text("#{pass_count} passed")
41
+ .escape_code(:reset_fg)
42
+ else
43
+ writer.text("#{pass_count} passed")
44
+ end
45
+
46
+ writer.text(", ")
47
+
48
+ if skip_count.nonzero? && !failed
49
+ writer
50
+ .escape_code(:yellow)
51
+ .text("#{skip_count} skipped")
52
+ .escape_code(:reset_fg)
53
+ else
54
+ writer.text("#{skip_count} skipped")
55
+ end
56
+
57
+ writer.text(", ")
58
+
59
+ if failure_count.nonzero?
60
+ writer
61
+ .escape_code(:bold)
62
+ .text("#{failure_count} failed")
63
+ .escape_code(:reset_intensity)
64
+ else
65
+ writer.text("0 failed")
66
+ end
67
+
68
+ writer.text(", ")
69
+
70
+ if failed
71
+ writer
72
+ .escape_code(:bold)
73
+ .text(numeric_label(error_count, 'total error'))
74
+ .escape_code(:reset_intensity)
75
+ .escape_code(:reset_fg)
76
+ else
77
+ writer.text("0 total errors")
78
+ end
79
+
80
+ 2.times do
81
+ writer.newline
82
+ end
83
+ end
84
+
85
+ def numeric_label(number, label, plural_text=nil)
86
+ plural_text ||= 's'
87
+
88
+ if number == 1
89
+ "#{number} #{label}"
90
+ else
91
+ "#{number} #{label}#{plural_text}"
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end