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.
- checksums.yaml +7 -0
- data/lib/test_bench.rb +37 -0
- data/lib/test_bench/cli.rb +43 -0
- data/lib/test_bench/cli/parse_arguments.rb +168 -0
- data/lib/test_bench/controls.rb +22 -0
- data/lib/test_bench/controls/caller_location.rb +5 -0
- data/lib/test_bench/controls/depth.rb +21 -0
- data/lib/test_bench/controls/device.rb +27 -0
- data/lib/test_bench/controls/directory.rb +15 -0
- data/lib/test_bench/controls/error.rb +35 -0
- data/lib/test_bench/controls/fixture.rb +29 -0
- data/lib/test_bench/controls/output/escape_code.rb +23 -0
- data/lib/test_bench/controls/output/newline_character.rb +11 -0
- data/lib/test_bench/controls/output/print_error.rb +19 -0
- data/lib/test_bench/controls/output/styling.rb +29 -0
- data/lib/test_bench/controls/output/summary/error.rb +44 -0
- data/lib/test_bench/controls/output/summary/run.rb +59 -0
- data/lib/test_bench/controls/path.rb +15 -0
- data/lib/test_bench/controls/pattern.rb +29 -0
- data/lib/test_bench/controls/result.rb +5 -0
- data/lib/test_bench/controls/test_file.rb +5 -0
- data/lib/test_bench/controls/time.rb +61 -0
- data/lib/test_bench/deactivation_variants.rb +21 -0
- data/lib/test_bench/environment/boolean.rb +40 -0
- data/lib/test_bench/fixtures.rb +3 -0
- data/lib/test_bench/fixtures/configure_receiver.rb +80 -0
- data/lib/test_bench/output.rb +9 -0
- data/lib/test_bench/output/build.rb +57 -0
- data/lib/test_bench/output/levels/debug.rb +229 -0
- data/lib/test_bench/output/levels/failure.rb +31 -0
- data/lib/test_bench/output/levels/none.rb +13 -0
- data/lib/test_bench/output/levels/pass.rb +274 -0
- data/lib/test_bench/output/levels/summary.rb +21 -0
- data/lib/test_bench/output/print_error.rb +163 -0
- data/lib/test_bench/output/summary/error.rb +77 -0
- data/lib/test_bench/output/summary/run.rb +102 -0
- data/lib/test_bench/output/summary/run/print.rb +98 -0
- data/lib/test_bench/output/timer.rb +75 -0
- data/lib/test_bench/output/timer/substitute.rb +45 -0
- data/lib/test_bench/output/writer.rb +192 -0
- data/lib/test_bench/output/writer/dependency.rb +12 -0
- data/lib/test_bench/output/writer/sgr.rb +54 -0
- data/lib/test_bench/output/writer/substitute.rb +38 -0
- data/lib/test_bench/run.rb +103 -0
- data/lib/test_bench/run/substitute.rb +36 -0
- data/lib/test_bench/test_bench.rb +76 -0
- data/script/bench +19 -0
- 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
|