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.
Files changed (50) hide show
  1. checksums.yaml +7 -0
  2. data/bin/bench +10 -0
  3. data/lib/test_bench.rb +37 -0
  4. data/lib/test_bench/activate.rb +3 -0
  5. data/lib/test_bench/assert.rb +58 -0
  6. data/lib/test_bench/assert/failed.rb +25 -0
  7. data/lib/test_bench/assert/proc.rb +24 -0
  8. data/lib/test_bench/assert/refute.rb +9 -0
  9. data/lib/test_bench/cli.rb +4 -0
  10. data/lib/test_bench/cli/cli.rb +108 -0
  11. data/lib/test_bench/controls.rb +16 -0
  12. data/lib/test_bench/controls/binding.rb +17 -0
  13. data/lib/test_bench/controls/clock/elapsed.rb +41 -0
  14. data/lib/test_bench/controls/clock/reference.rb +15 -0
  15. data/lib/test_bench/controls/dir_substitute.rb +58 -0
  16. data/lib/test_bench/controls/error.rb +63 -0
  17. data/lib/test_bench/controls/executor/substitute.rb +27 -0
  18. data/lib/test_bench/controls/expand_path.rb +23 -0
  19. data/lib/test_bench/controls/fixture.rb +36 -0
  20. data/lib/test_bench/controls/kernel_substitute.rb +72 -0
  21. data/lib/test_bench/controls/output.rb +106 -0
  22. data/lib/test_bench/controls/path.rb +11 -0
  23. data/lib/test_bench/controls/result.rb +64 -0
  24. data/lib/test_bench/controls/telemetry.rb +23 -0
  25. data/lib/test_bench/controls/test_script.rb +23 -0
  26. data/lib/test_bench/executor.rb +45 -0
  27. data/lib/test_bench/expand_path.rb +44 -0
  28. data/lib/test_bench/fixture.rb +17 -0
  29. data/lib/test_bench/output.rb +169 -0
  30. data/lib/test_bench/output/assertions.rb +43 -0
  31. data/lib/test_bench/output/palette.rb +59 -0
  32. data/lib/test_bench/output/writer.rb +94 -0
  33. data/lib/test_bench/output/writer/assertions.rb +29 -0
  34. data/lib/test_bench/output/writer/assertions/line.rb +76 -0
  35. data/lib/test_bench/registry.rb +32 -0
  36. data/lib/test_bench/result.rb +71 -0
  37. data/lib/test_bench/result/assertions.rb +11 -0
  38. data/lib/test_bench/result/null.rb +12 -0
  39. data/lib/test_bench/runner.rb +59 -0
  40. data/lib/test_bench/settings.rb +62 -0
  41. data/lib/test_bench/settings/defaults.rb +29 -0
  42. data/lib/test_bench/settings/environment.rb +124 -0
  43. data/lib/test_bench/settings/registry.rb +17 -0
  44. data/lib/test_bench/structure.rb +89 -0
  45. data/lib/test_bench/telemetry.rb +130 -0
  46. data/lib/test_bench/telemetry/assertions.rb +85 -0
  47. data/lib/test_bench/telemetry/registry.rb +17 -0
  48. data/lib/test_bench/telemetry/subscription.rb +23 -0
  49. data/lib/test_bench/test_bench.rb +33 -0
  50. 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