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,75 @@
|
|
1
|
+
module TestBench
|
2
|
+
module Output
|
3
|
+
class Timer
|
4
|
+
Error = Class.new(RuntimeError)
|
5
|
+
|
6
|
+
attr_accessor :start_time
|
7
|
+
|
8
|
+
def self.configure(receiver, attr_name: nil)
|
9
|
+
attr_name ||= :timer
|
10
|
+
|
11
|
+
instance = new
|
12
|
+
receiver.public_send(:"#{attr_name}=", instance)
|
13
|
+
instance
|
14
|
+
end
|
15
|
+
|
16
|
+
def start(now=nil)
|
17
|
+
now ||= ::Time.now.utc
|
18
|
+
|
19
|
+
if mode == Mode.running
|
20
|
+
raise Error, "Timer has already started (Start Time: #{start_time})"
|
21
|
+
end
|
22
|
+
|
23
|
+
self.start_time = now
|
24
|
+
end
|
25
|
+
|
26
|
+
def stop(now=nil)
|
27
|
+
now ||= ::Time.now.utc
|
28
|
+
|
29
|
+
if mode == Mode.stopped
|
30
|
+
raise Error, "Timer has not started"
|
31
|
+
end
|
32
|
+
|
33
|
+
start_time = reset
|
34
|
+
|
35
|
+
elapsed = now - start_time
|
36
|
+
|
37
|
+
elapsed.round(3)
|
38
|
+
end
|
39
|
+
|
40
|
+
def running?
|
41
|
+
mode == Mode.running
|
42
|
+
end
|
43
|
+
|
44
|
+
def stopped?
|
45
|
+
mode == Mode.stopped
|
46
|
+
end
|
47
|
+
|
48
|
+
def mode
|
49
|
+
if start_time.nil?
|
50
|
+
Mode.stopped
|
51
|
+
else
|
52
|
+
Mode.running
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def reset
|
57
|
+
previous_start_time = self.start_time
|
58
|
+
|
59
|
+
self.start_time = nil
|
60
|
+
|
61
|
+
previous_start_time
|
62
|
+
end
|
63
|
+
|
64
|
+
module Mode
|
65
|
+
def self.running
|
66
|
+
:running
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.stopped
|
70
|
+
:stopped
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module TestBench
|
2
|
+
module Output
|
3
|
+
class Timer
|
4
|
+
module Substitute
|
5
|
+
def self.build
|
6
|
+
Timer.new
|
7
|
+
end
|
8
|
+
|
9
|
+
class Timer < Timer
|
10
|
+
def elapsed_time
|
11
|
+
@elapsed_time ||= 0
|
12
|
+
end
|
13
|
+
attr_writer :elapsed_time
|
14
|
+
|
15
|
+
def mode
|
16
|
+
@mode ||= Mode.stopped
|
17
|
+
end
|
18
|
+
attr_writer :mode
|
19
|
+
|
20
|
+
def start(_=nil)
|
21
|
+
if mode == Mode.running
|
22
|
+
raise Error, "Timer has already started"
|
23
|
+
end
|
24
|
+
|
25
|
+
self.mode = Mode.running
|
26
|
+
end
|
27
|
+
|
28
|
+
def stop(_=nil)
|
29
|
+
if mode == Mode.stopped
|
30
|
+
raise Error, "Timer has not started"
|
31
|
+
end
|
32
|
+
|
33
|
+
self.mode = Mode.stopped
|
34
|
+
|
35
|
+
elapsed_time.round(3)
|
36
|
+
end
|
37
|
+
|
38
|
+
def set(elapsed_time)
|
39
|
+
self.elapsed_time = elapsed_time
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,192 @@
|
|
1
|
+
module TestBench
|
2
|
+
module Output
|
3
|
+
class Writer
|
4
|
+
Error = Class.new(RuntimeError)
|
5
|
+
|
6
|
+
def device
|
7
|
+
@device ||= StringIO.new
|
8
|
+
end
|
9
|
+
attr_writer :device
|
10
|
+
|
11
|
+
attr_accessor :styling
|
12
|
+
|
13
|
+
def styling_enabled
|
14
|
+
instance_variable_defined?(:@styling_enabled) ?
|
15
|
+
@styling_enabled :
|
16
|
+
@styling_enabled = self.class.styling?(device, styling)
|
17
|
+
end
|
18
|
+
attr_writer :styling_enabled
|
19
|
+
alias_method :styling?, :styling_enabled
|
20
|
+
|
21
|
+
def mode
|
22
|
+
@mode ||= Mode.text
|
23
|
+
end
|
24
|
+
attr_writer :mode
|
25
|
+
|
26
|
+
def byte_offset
|
27
|
+
@byte_offset ||= 0
|
28
|
+
end
|
29
|
+
attr_writer :byte_offset
|
30
|
+
|
31
|
+
def indentation_depth
|
32
|
+
@indentation_depth ||= 0
|
33
|
+
end
|
34
|
+
attr_writer :indentation_depth
|
35
|
+
|
36
|
+
def configure(styling: nil, device: nil)
|
37
|
+
device ||= Defaults.device
|
38
|
+
|
39
|
+
unless styling.nil?
|
40
|
+
self.class.assure_styling_setting(styling)
|
41
|
+
end
|
42
|
+
|
43
|
+
self.device = device
|
44
|
+
self.styling = styling
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.build(device=nil, styling: nil)
|
48
|
+
instance = new
|
49
|
+
instance.configure(device: device, styling: styling)
|
50
|
+
instance
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.configure(receiver, writer: nil, styling: nil, device: nil, attr_name: nil)
|
54
|
+
attr_name ||= :writer
|
55
|
+
|
56
|
+
if writer.nil?
|
57
|
+
instance = build(device, styling: styling)
|
58
|
+
else
|
59
|
+
instance = writer
|
60
|
+
end
|
61
|
+
|
62
|
+
receiver.public_send(:"#{attr_name}=", instance)
|
63
|
+
|
64
|
+
instance
|
65
|
+
end
|
66
|
+
|
67
|
+
def text(text)
|
68
|
+
if mode == Mode.escape_sequence
|
69
|
+
self.mode = Mode.text
|
70
|
+
|
71
|
+
write('m')
|
72
|
+
end
|
73
|
+
|
74
|
+
write(text)
|
75
|
+
|
76
|
+
self
|
77
|
+
end
|
78
|
+
|
79
|
+
def escape_code(id)
|
80
|
+
escape_code = SGR.escape_code(id)
|
81
|
+
|
82
|
+
if mode == Mode.text
|
83
|
+
return self unless styling?
|
84
|
+
|
85
|
+
self.mode = Mode.escape_sequence
|
86
|
+
|
87
|
+
write("\e[")
|
88
|
+
else
|
89
|
+
write(';')
|
90
|
+
end
|
91
|
+
|
92
|
+
write(escape_code)
|
93
|
+
|
94
|
+
self
|
95
|
+
end
|
96
|
+
|
97
|
+
def sync
|
98
|
+
text('')
|
99
|
+
end
|
100
|
+
|
101
|
+
def indent
|
102
|
+
indentation = ' ' * indentation_depth
|
103
|
+
|
104
|
+
text(indentation)
|
105
|
+
end
|
106
|
+
|
107
|
+
def newline
|
108
|
+
sync
|
109
|
+
device.puts
|
110
|
+
self.byte_offset += 1
|
111
|
+
self
|
112
|
+
end
|
113
|
+
|
114
|
+
def write(data)
|
115
|
+
bytes_written = device.write(data)
|
116
|
+
|
117
|
+
self.byte_offset += bytes_written
|
118
|
+
end
|
119
|
+
|
120
|
+
def current?(byte_offset)
|
121
|
+
byte_offset >= self.byte_offset
|
122
|
+
end
|
123
|
+
|
124
|
+
def increase_indentation
|
125
|
+
self.indentation_depth += 1
|
126
|
+
end
|
127
|
+
|
128
|
+
def decrease_indentation
|
129
|
+
self.indentation_depth -= 1
|
130
|
+
end
|
131
|
+
|
132
|
+
def self.styling?(device, styling_setting=nil)
|
133
|
+
styling_setting ||= Defaults.styling_setting
|
134
|
+
|
135
|
+
assure_styling_setting(styling_setting)
|
136
|
+
|
137
|
+
case styling_setting
|
138
|
+
when :detect
|
139
|
+
device.tty?
|
140
|
+
when :on
|
141
|
+
true
|
142
|
+
when :off
|
143
|
+
false
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
def self.assure_styling_setting(styling_setting)
|
148
|
+
unless styling_settings.include?(styling_setting)
|
149
|
+
raise Error, "Invalid output styling #{styling_setting.inspect} (Valid values: #{styling_settings.map(&:inspect).join(', ')})"
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
def self.styling_settings
|
154
|
+
[
|
155
|
+
:detect,
|
156
|
+
:on,
|
157
|
+
:off
|
158
|
+
]
|
159
|
+
end
|
160
|
+
|
161
|
+
def self.default_styling_setting
|
162
|
+
styling_settings.fetch(0)
|
163
|
+
end
|
164
|
+
|
165
|
+
module Mode
|
166
|
+
def self.text
|
167
|
+
:text
|
168
|
+
end
|
169
|
+
|
170
|
+
def self.escape_sequence
|
171
|
+
:escape_sequence
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
module Defaults
|
176
|
+
def self.device
|
177
|
+
$stdout
|
178
|
+
end
|
179
|
+
|
180
|
+
def self.styling_setting
|
181
|
+
styling = ::ENV['TEST_BENCH_OUTPUT_STYLING']
|
182
|
+
|
183
|
+
if styling.nil?
|
184
|
+
Writer.default_styling_setting
|
185
|
+
else
|
186
|
+
styling.to_sym
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module TestBench
|
2
|
+
module Output
|
3
|
+
class Writer
|
4
|
+
module SGR
|
5
|
+
def self.escape_code(name)
|
6
|
+
assure_escape_code(name)
|
7
|
+
|
8
|
+
code_map.fetch(name)
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.assure_escape_code(name)
|
12
|
+
unless code_map.key?(name)
|
13
|
+
raise Error, "Invalid escape code #{name.inspect} (Example values: #{code_map.keys.first(3).map(&:inspect).join(', ')})"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.code_map
|
18
|
+
@code_map ||= {
|
19
|
+
:reset => '0',
|
20
|
+
|
21
|
+
:bold => '1',
|
22
|
+
:faint => '2',
|
23
|
+
:italic => '3',
|
24
|
+
:underline => '4',
|
25
|
+
|
26
|
+
:reset_intensity => '22',
|
27
|
+
:reset_italic => '23',
|
28
|
+
:reset_underline => '24',
|
29
|
+
|
30
|
+
:black => '30',
|
31
|
+
:red => '31',
|
32
|
+
:green => '32',
|
33
|
+
:yellow => '33',
|
34
|
+
:blue => '34',
|
35
|
+
:magenta => '35',
|
36
|
+
:cyan => '36',
|
37
|
+
:white => '37',
|
38
|
+
:reset_fg => '39',
|
39
|
+
|
40
|
+
:black_bg => '40',
|
41
|
+
:red_bg => '41',
|
42
|
+
:green_bg => '42',
|
43
|
+
:yellow_bg => '43',
|
44
|
+
:blue_bg => '44',
|
45
|
+
:magenta_bg => '45',
|
46
|
+
:cyan_bg => '46',
|
47
|
+
:white_bg => '47',
|
48
|
+
:reset_bg => '49'
|
49
|
+
}
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module TestBench
|
2
|
+
module Output
|
3
|
+
class Writer
|
4
|
+
module Substitute
|
5
|
+
def self.build
|
6
|
+
writer = Writer.new
|
7
|
+
writer.styling_enabled = false
|
8
|
+
writer
|
9
|
+
end
|
10
|
+
|
11
|
+
class Writer < Writer
|
12
|
+
def written?(pattern=nil)
|
13
|
+
pattern = self.pattern(pattern)
|
14
|
+
|
15
|
+
written_text = device.string
|
16
|
+
|
17
|
+
pattern.match?(written_text)
|
18
|
+
end
|
19
|
+
|
20
|
+
def pattern(pattern)
|
21
|
+
case pattern
|
22
|
+
when nil
|
23
|
+
/./
|
24
|
+
when String
|
25
|
+
Regexp.new("\\A#{Regexp.escape(pattern)}\\z")
|
26
|
+
else
|
27
|
+
pattern
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def enable_styling!
|
32
|
+
self.styling_enabled = true
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
module TestBench
|
2
|
+
class Run
|
3
|
+
Error = Class.new(RuntimeError)
|
4
|
+
|
5
|
+
def session
|
6
|
+
@session ||= Session::Substitute.build
|
7
|
+
end
|
8
|
+
attr_writer :session
|
9
|
+
|
10
|
+
def exclude_file_pattern
|
11
|
+
@exclude_file_pattern ||= Defaults.exclude_file_pattern
|
12
|
+
end
|
13
|
+
attr_writer :exclude_file_pattern
|
14
|
+
|
15
|
+
attr_reader :paths
|
16
|
+
|
17
|
+
def initialize(*paths)
|
18
|
+
@paths = Array(paths)
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.build(*paths, exclude_file_pattern: nil, session: nil, output: nil)
|
22
|
+
session ||= TestBench.session
|
23
|
+
|
24
|
+
instance = new(*paths)
|
25
|
+
|
26
|
+
instance.exclude_file_pattern = exclude_file_pattern unless exclude_file_pattern.nil?
|
27
|
+
|
28
|
+
Session.configure(instance, session: session)
|
29
|
+
instance.session.output = output unless output.nil?
|
30
|
+
|
31
|
+
instance
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.configure(receiver, *paths, attr_name: nil, **args)
|
35
|
+
attr_name ||= :run
|
36
|
+
|
37
|
+
instance = build(*paths, **args)
|
38
|
+
receiver.public_send(:"#{attr_name}=", instance)
|
39
|
+
instance
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.call(*paths, **args, &block)
|
43
|
+
instance = build(*paths, **args)
|
44
|
+
|
45
|
+
if block.nil?
|
46
|
+
instance.()
|
47
|
+
else
|
48
|
+
instance.() do
|
49
|
+
block.(instance)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def call(&block)
|
55
|
+
session.start
|
56
|
+
|
57
|
+
if block.nil?
|
58
|
+
paths.each do |path|
|
59
|
+
path(path)
|
60
|
+
end
|
61
|
+
else
|
62
|
+
block.()
|
63
|
+
end
|
64
|
+
|
65
|
+
ensure
|
66
|
+
session.finish
|
67
|
+
end
|
68
|
+
|
69
|
+
def path(path)
|
70
|
+
if File.directory?(path)
|
71
|
+
directory(path)
|
72
|
+
elsif File.exist?(path)
|
73
|
+
file(path)
|
74
|
+
else
|
75
|
+
raise Error, "Path not found (Path: #{path.inspect})"
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def directory(path)
|
80
|
+
glob_pattern = File.join(path, '**/*.rb')
|
81
|
+
|
82
|
+
Dir[glob_pattern].sort.each do |path|
|
83
|
+
next if exclude_file_pattern.match?(path)
|
84
|
+
|
85
|
+
file(path)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def file(path)
|
90
|
+
session.load(path)
|
91
|
+
end
|
92
|
+
|
93
|
+
module Defaults
|
94
|
+
def self.exclude_file_pattern
|
95
|
+
pattern = ENV.fetch('TEST_BENCH_EXCLUDE_FILE_PATTERN') do
|
96
|
+
'_init.rb$'
|
97
|
+
end
|
98
|
+
|
99
|
+
Regexp.new(pattern)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|