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,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,12 @@
1
+ module TestBench
2
+ module Output
3
+ class Writer
4
+ module Dependency
5
+ def writer
6
+ @writer ||= Substitute.build
7
+ end
8
+ attr_writer :writer
9
+ end
10
+ end
11
+ end
12
+ 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