test_bench 1.0.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|