test_bench_legacy 1.3.5
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/bin/bench +10 -0
- data/lib/test_bench.rb +37 -0
- data/lib/test_bench/activate.rb +3 -0
- data/lib/test_bench/assert.rb +58 -0
- data/lib/test_bench/assert/failed.rb +25 -0
- data/lib/test_bench/assert/proc.rb +24 -0
- data/lib/test_bench/assert/refute.rb +9 -0
- data/lib/test_bench/cli.rb +4 -0
- data/lib/test_bench/cli/cli.rb +108 -0
- data/lib/test_bench/controls.rb +16 -0
- data/lib/test_bench/controls/binding.rb +17 -0
- data/lib/test_bench/controls/clock/elapsed.rb +41 -0
- data/lib/test_bench/controls/clock/reference.rb +15 -0
- data/lib/test_bench/controls/dir_substitute.rb +58 -0
- data/lib/test_bench/controls/error.rb +63 -0
- data/lib/test_bench/controls/executor/substitute.rb +27 -0
- data/lib/test_bench/controls/expand_path.rb +23 -0
- data/lib/test_bench/controls/fixture.rb +36 -0
- data/lib/test_bench/controls/kernel_substitute.rb +72 -0
- data/lib/test_bench/controls/output.rb +106 -0
- data/lib/test_bench/controls/path.rb +11 -0
- data/lib/test_bench/controls/result.rb +64 -0
- data/lib/test_bench/controls/telemetry.rb +23 -0
- data/lib/test_bench/controls/test_script.rb +23 -0
- data/lib/test_bench/executor.rb +45 -0
- data/lib/test_bench/expand_path.rb +44 -0
- data/lib/test_bench/fixture.rb +17 -0
- data/lib/test_bench/output.rb +169 -0
- data/lib/test_bench/output/assertions.rb +43 -0
- data/lib/test_bench/output/palette.rb +59 -0
- data/lib/test_bench/output/writer.rb +94 -0
- data/lib/test_bench/output/writer/assertions.rb +29 -0
- data/lib/test_bench/output/writer/assertions/line.rb +76 -0
- data/lib/test_bench/registry.rb +32 -0
- data/lib/test_bench/result.rb +71 -0
- data/lib/test_bench/result/assertions.rb +11 -0
- data/lib/test_bench/result/null.rb +12 -0
- data/lib/test_bench/runner.rb +59 -0
- data/lib/test_bench/settings.rb +62 -0
- data/lib/test_bench/settings/defaults.rb +29 -0
- data/lib/test_bench/settings/environment.rb +124 -0
- data/lib/test_bench/settings/registry.rb +17 -0
- data/lib/test_bench/structure.rb +89 -0
- data/lib/test_bench/telemetry.rb +130 -0
- data/lib/test_bench/telemetry/assertions.rb +85 -0
- data/lib/test_bench/telemetry/registry.rb +17 -0
- data/lib/test_bench/telemetry/subscription.rb +23 -0
- data/lib/test_bench/test_bench.rb +33 -0
- metadata +94 -0
@@ -0,0 +1,45 @@
|
|
1
|
+
module TestBench
|
2
|
+
class Executor
|
3
|
+
attr_reader :binding
|
4
|
+
attr_reader :kernel
|
5
|
+
|
6
|
+
def initialize binding, kernel
|
7
|
+
@binding = binding
|
8
|
+
@kernel = kernel
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.build
|
12
|
+
binding = TOPLEVEL_BINDING
|
13
|
+
|
14
|
+
new binding, Kernel
|
15
|
+
end
|
16
|
+
|
17
|
+
def call files
|
18
|
+
files.each do |file|
|
19
|
+
execute file
|
20
|
+
end
|
21
|
+
|
22
|
+
telemetry.passed?
|
23
|
+
end
|
24
|
+
|
25
|
+
def execute file
|
26
|
+
telemetry.file_started file
|
27
|
+
|
28
|
+
begin
|
29
|
+
unbound_context_method = TestBench::Structure.instance_method :context
|
30
|
+
bound_context_method = unbound_context_method.bind binding.receiver
|
31
|
+
|
32
|
+
bound_context_method.call :suppress_exit => true do
|
33
|
+
kernel.load File.expand_path file
|
34
|
+
end
|
35
|
+
|
36
|
+
ensure
|
37
|
+
telemetry.file_finished file
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def telemetry
|
42
|
+
Telemetry::Registry.get binding
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module TestBench
|
2
|
+
class ExpandPath
|
3
|
+
attr_reader :dir
|
4
|
+
attr_reader :exclude_pattern
|
5
|
+
attr_reader :root_directory
|
6
|
+
|
7
|
+
def initialize root_directory, exclude_pattern, dir
|
8
|
+
@dir = dir
|
9
|
+
@exclude_pattern = exclude_pattern
|
10
|
+
@root_directory = root_directory
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.build root_directory, exclude_pattern=nil, dir: nil
|
14
|
+
dir ||= Dir
|
15
|
+
|
16
|
+
exclude_pattern ||= Settings.toplevel.exclude_pattern
|
17
|
+
exclude_pattern = Regexp.new exclude_pattern if exclude_pattern.is_a? String
|
18
|
+
|
19
|
+
root_directory = Pathname root_directory
|
20
|
+
|
21
|
+
new root_directory, exclude_pattern, dir
|
22
|
+
end
|
23
|
+
|
24
|
+
def call pattern
|
25
|
+
full_pattern = root_directory.join pattern
|
26
|
+
|
27
|
+
if dir.exist? full_pattern
|
28
|
+
full_pattern = full_pattern.join '**/*.rb'
|
29
|
+
end
|
30
|
+
|
31
|
+
expand full_pattern.to_s
|
32
|
+
end
|
33
|
+
|
34
|
+
def expand full_pattern
|
35
|
+
dir[full_pattern].flat_map do |file|
|
36
|
+
if exclude_pattern.match file
|
37
|
+
[]
|
38
|
+
else
|
39
|
+
[file]
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module TestBench
|
2
|
+
module Fixture
|
3
|
+
def self.included cls
|
4
|
+
cls.class_exec do
|
5
|
+
extend Forwardable
|
6
|
+
|
7
|
+
delegate %i(assert comment context refute test) => :structure
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
attr_writer :structure
|
12
|
+
|
13
|
+
def structure
|
14
|
+
@structure ||= TOPLEVEL_BINDING.receiver
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,169 @@
|
|
1
|
+
module TestBench
|
2
|
+
class Output
|
3
|
+
attr_writer :file_result
|
4
|
+
attr_writer :reverse_backtraces
|
5
|
+
attr_writer :run_result
|
6
|
+
attr_writer :writer
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@file_result = nil
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.build level=nil
|
13
|
+
writer = Writer.build $stdout
|
14
|
+
writer.level = level if level
|
15
|
+
|
16
|
+
instance = new
|
17
|
+
instance.writer = writer
|
18
|
+
instance
|
19
|
+
end
|
20
|
+
|
21
|
+
def asserted
|
22
|
+
file_result.asserted
|
23
|
+
run_result.asserted
|
24
|
+
end
|
25
|
+
|
26
|
+
def commented prose
|
27
|
+
writer.normal prose, :fg => :normal
|
28
|
+
end
|
29
|
+
|
30
|
+
def context_entered prose=nil
|
31
|
+
return if prose.nil?
|
32
|
+
|
33
|
+
writer.normal prose, :fg => :green
|
34
|
+
|
35
|
+
writer.increase_indentation unless writer.level == :quiet
|
36
|
+
end
|
37
|
+
|
38
|
+
def context_exited prose=nil
|
39
|
+
return if prose.nil?
|
40
|
+
|
41
|
+
writer.decrease_indentation unless writer.level == :quiet
|
42
|
+
|
43
|
+
writer.normal ' ' if writer.indentation.zero?
|
44
|
+
end
|
45
|
+
|
46
|
+
def device
|
47
|
+
@device ||= StringIO.new
|
48
|
+
end
|
49
|
+
|
50
|
+
def error_raised error
|
51
|
+
run_result.error_raised error
|
52
|
+
file_result.error_raised error
|
53
|
+
|
54
|
+
detail_summary = "#{error.backtrace[0]}: #{error.message} (#{error.class})"
|
55
|
+
|
56
|
+
lines = [detail_summary]
|
57
|
+
error.backtrace[1..-1].each do |frame|
|
58
|
+
lines << " from #{frame}"
|
59
|
+
end
|
60
|
+
|
61
|
+
lines.reverse! if reverse_backtraces
|
62
|
+
|
63
|
+
lines.each do |line|
|
64
|
+
writer.quiet line, :fg => :red
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def file_finished path
|
69
|
+
run_result.file_finished path
|
70
|
+
file_result.finished
|
71
|
+
|
72
|
+
summary = summarize_result file_result
|
73
|
+
|
74
|
+
self.file_result = nil
|
75
|
+
|
76
|
+
writer.verbose "Finished running #{path}"
|
77
|
+
writer.verbose summary
|
78
|
+
writer.verbose ' '
|
79
|
+
end
|
80
|
+
|
81
|
+
def file_result
|
82
|
+
@file_result or Result::Null
|
83
|
+
end
|
84
|
+
|
85
|
+
def file_started path
|
86
|
+
writer.normal "Running #{path}"
|
87
|
+
|
88
|
+
file_result = Result.build
|
89
|
+
|
90
|
+
self.file_result = file_result
|
91
|
+
|
92
|
+
file_result
|
93
|
+
end
|
94
|
+
|
95
|
+
def reverse_backtraces
|
96
|
+
ivar = :@reverse_backtraces
|
97
|
+
|
98
|
+
if instance_variable_defined? ivar
|
99
|
+
instance_variable_get ivar
|
100
|
+
else
|
101
|
+
instance_variable_set ivar, false
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def run_finished
|
106
|
+
run_result.run_finished
|
107
|
+
|
108
|
+
files_label = if run_result.files.size == 1 then 'file' else 'files' end
|
109
|
+
|
110
|
+
color = if run_result.passed? then :cyan else :red end
|
111
|
+
|
112
|
+
writer.quiet "Finished running #{run_result.files.size} #{files_label}"
|
113
|
+
|
114
|
+
summary = summarize_result run_result
|
115
|
+
|
116
|
+
writer.quiet summary, :fg => color
|
117
|
+
end
|
118
|
+
|
119
|
+
def run_started
|
120
|
+
self.run_result
|
121
|
+
end
|
122
|
+
|
123
|
+
def run_result
|
124
|
+
@run_result ||= Result.build
|
125
|
+
end
|
126
|
+
|
127
|
+
def summarize_result result
|
128
|
+
minutes, seconds = result.elapsed_time.divmod 60
|
129
|
+
|
130
|
+
elapsed = String.new
|
131
|
+
elapsed << "#{minutes}m" unless minutes.zero?
|
132
|
+
elapsed << "%.3fs" % seconds
|
133
|
+
|
134
|
+
test_label = if result.tests == 1 then 'test' else 'tests' end
|
135
|
+
error_label = if result.errors == 1 then 'error' else 'errors' end
|
136
|
+
"Ran %d #{test_label} in #{elapsed} (%.3fs tests/second)\n%d passed, %d skipped, %d failed, %d total #{error_label}" %
|
137
|
+
[result.tests, result.tests_per_second, result.passes, result.skips, result.failures, result.errors]
|
138
|
+
end
|
139
|
+
|
140
|
+
def test_failed prose
|
141
|
+
file_result.test_failed prose
|
142
|
+
run_result.test_failed prose
|
143
|
+
|
144
|
+
writer.quiet prose, :fg => :white, :bg => :red
|
145
|
+
end
|
146
|
+
|
147
|
+
def test_passed prose
|
148
|
+
file_result.test_passed prose
|
149
|
+
run_result.test_passed prose
|
150
|
+
|
151
|
+
writer.normal prose, :fg => :green
|
152
|
+
end
|
153
|
+
|
154
|
+
def test_skipped prose
|
155
|
+
file_result.test_skipped prose
|
156
|
+
run_result.test_skipped prose
|
157
|
+
|
158
|
+
writer.normal prose, :fg => :brown
|
159
|
+
end
|
160
|
+
|
161
|
+
def test_started prose
|
162
|
+
writer.verbose "Started test #{prose.inspect}", :fg => :gray
|
163
|
+
end
|
164
|
+
|
165
|
+
def writer
|
166
|
+
@writer ||= Writer.new
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module TestBench
|
2
|
+
class Output
|
3
|
+
module Assertions
|
4
|
+
def color_output?
|
5
|
+
text = 'Some Text'
|
6
|
+
color = :gray
|
7
|
+
|
8
|
+
write text, :fg => color, :bg => color
|
9
|
+
|
10
|
+
wrote_line? text, :fg => color, :bg => color
|
11
|
+
end
|
12
|
+
|
13
|
+
def text_written
|
14
|
+
device.rewind
|
15
|
+
device.read
|
16
|
+
end
|
17
|
+
|
18
|
+
def wrote? text
|
19
|
+
text_written == text
|
20
|
+
end
|
21
|
+
|
22
|
+
def wrote_line? text, indent: nil, **colors
|
23
|
+
color ||= {}
|
24
|
+
indent ||= 0
|
25
|
+
|
26
|
+
color_escape = Palette.escape_code(**colors)
|
27
|
+
unless color_escape.empty?
|
28
|
+
text = "#{color_escape}#{text}\e[0m"
|
29
|
+
end
|
30
|
+
|
31
|
+
matcher = Regexp.escape "#{' ' * indent}#{text}"
|
32
|
+
|
33
|
+
pattern = /^#{matcher}$/n
|
34
|
+
|
35
|
+
pattern.match text_written
|
36
|
+
end
|
37
|
+
|
38
|
+
def wrote_nothing?
|
39
|
+
text_written.empty?
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module TestBench
|
2
|
+
class Output
|
3
|
+
module Palette
|
4
|
+
def self.apply prose, **colors
|
5
|
+
escape_code = self.escape_code(**colors)
|
6
|
+
|
7
|
+
if escape_code.empty?
|
8
|
+
prose
|
9
|
+
else
|
10
|
+
"#{escape_code}#{prose}\e[0m"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.escape_code fg: nil, bg: nil
|
15
|
+
return '' if fg.nil? and bg.nil?
|
16
|
+
|
17
|
+
brightness, fg = get fg if fg
|
18
|
+
_, bg = get bg if bg
|
19
|
+
|
20
|
+
str = String.new "\e["
|
21
|
+
|
22
|
+
str << "#{brightness};3#{fg}" if fg
|
23
|
+
str << ';' if fg and bg
|
24
|
+
str << "4#{bg}" if bg
|
25
|
+
str << 'm'
|
26
|
+
|
27
|
+
str
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.get name
|
31
|
+
code = names.index name
|
32
|
+
return unless code
|
33
|
+
brightness, code = code.divmod 8
|
34
|
+
return brightness, code
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.names
|
38
|
+
@names ||= %i(
|
39
|
+
black
|
40
|
+
red
|
41
|
+
green
|
42
|
+
brown
|
43
|
+
blue
|
44
|
+
magenta
|
45
|
+
cyan
|
46
|
+
gray
|
47
|
+
dark_gray
|
48
|
+
bright_red
|
49
|
+
bright_green
|
50
|
+
yellow
|
51
|
+
bright_blue
|
52
|
+
bright_magenta
|
53
|
+
bright_cyan
|
54
|
+
white
|
55
|
+
)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
module TestBench
|
2
|
+
class Output
|
3
|
+
class Writer
|
4
|
+
attr_accessor :color
|
5
|
+
attr_writer :device
|
6
|
+
attr_accessor :indentation
|
7
|
+
attr_writer :level
|
8
|
+
attr_reader :mutex
|
9
|
+
|
10
|
+
def initialize level=nil
|
11
|
+
@level = level
|
12
|
+
@indentation = 0
|
13
|
+
@mutex = Mutex.new
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.build device
|
17
|
+
instance = new
|
18
|
+
instance.device = device
|
19
|
+
instance
|
20
|
+
end
|
21
|
+
|
22
|
+
def color?
|
23
|
+
return color unless color.nil?
|
24
|
+
device.tty?
|
25
|
+
end
|
26
|
+
|
27
|
+
def decrease_indentation
|
28
|
+
mutex.synchronize do
|
29
|
+
self.indentation -= 1
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def device
|
34
|
+
@device ||= StringIO.new
|
35
|
+
end
|
36
|
+
|
37
|
+
def increase_indentation
|
38
|
+
mutex.synchronize do
|
39
|
+
self.indentation += 1
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def level
|
44
|
+
@level ||= :normal
|
45
|
+
end
|
46
|
+
|
47
|
+
def lower_verbosity
|
48
|
+
if level == :verbose
|
49
|
+
self.level = :normal
|
50
|
+
elsif level == :normal
|
51
|
+
self.level = :quiet
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def normal prose, **arguments
|
56
|
+
arguments[:render] = false if level == :quiet
|
57
|
+
write prose, **arguments
|
58
|
+
end
|
59
|
+
|
60
|
+
def quiet prose, **arguments
|
61
|
+
write prose, **arguments
|
62
|
+
end
|
63
|
+
|
64
|
+
def raise_verbosity
|
65
|
+
if level == :quiet
|
66
|
+
self.level = :normal
|
67
|
+
elsif level == :normal
|
68
|
+
self.level = :verbose
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def verbose prose, **arguments
|
73
|
+
arguments[:render] = false unless level == :verbose
|
74
|
+
write prose, **arguments
|
75
|
+
end
|
76
|
+
|
77
|
+
def write prose, bg: nil, fg: nil, render: nil
|
78
|
+
render = true if render.nil?
|
79
|
+
|
80
|
+
if render
|
81
|
+
text = String.new prose
|
82
|
+
|
83
|
+
indentation = ' ' * self.indentation
|
84
|
+
|
85
|
+
prose = Palette.apply prose, bg: bg, fg: fg if color?
|
86
|
+
|
87
|
+
text = "#{indentation}#{prose}#{$INPUT_RECORD_SEPARATOR}"
|
88
|
+
|
89
|
+
device.write text
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|