test_bench-run 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 (35) hide show
  1. checksums.yaml +7 -0
  2. data/lib/test_bench/run/controls/directory.rb +54 -0
  3. data/lib/test_bench/run/controls/event_data.rb +7 -0
  4. data/lib/test_bench/run/controls/events/event_data.rb +9 -0
  5. data/lib/test_bench/run/controls/events/file_crashed.rb +109 -0
  6. data/lib/test_bench/run/controls/events/file_finished.rb +56 -0
  7. data/lib/test_bench/run/controls/events/file_started.rb +47 -0
  8. data/lib/test_bench/run/controls/events/finished.rb +50 -0
  9. data/lib/test_bench/run/controls/events/session.rb +9 -0
  10. data/lib/test_bench/run/controls/events/started.rb +41 -0
  11. data/lib/test_bench/run/controls/exception.rb +101 -0
  12. data/lib/test_bench/run/controls/executor.rb +56 -0
  13. data/lib/test_bench/run/controls/file/create.rb +69 -0
  14. data/lib/test_bench/run/controls/file/pattern.rb +29 -0
  15. data/lib/test_bench/run/controls/file.rb +180 -0
  16. data/lib/test_bench/run/controls/path.rb +15 -0
  17. data/lib/test_bench/run/controls/process_id.rb +7 -0
  18. data/lib/test_bench/run/controls/random.rb +7 -0
  19. data/lib/test_bench/run/controls/result.rb +7 -0
  20. data/lib/test_bench/run/controls/time.rb +7 -0
  21. data/lib/test_bench/run/controls.rb +26 -0
  22. data/lib/test_bench/run/events.rb +12 -0
  23. data/lib/test_bench/run/executor/serial.rb +112 -0
  24. data/lib/test_bench/run/executor/substitute.rb +45 -0
  25. data/lib/test_bench/run/executor.rb +44 -0
  26. data/lib/test_bench/run/file/substitute.rb +41 -0
  27. data/lib/test_bench/run/file.rb +73 -0
  28. data/lib/test_bench/run/get_files/substitute.rb +46 -0
  29. data/lib/test_bench/run/get_files.rb +72 -0
  30. data/lib/test_bench/run/output/file.rb +129 -0
  31. data/lib/test_bench/run/output/summary/error.rb +139 -0
  32. data/lib/test_bench/run/output/summary.rb +179 -0
  33. data/lib/test_bench/run/run.rb +101 -0
  34. data/lib/test_bench/run.rb +19 -0
  35. metadata +103 -0
@@ -0,0 +1,180 @@
1
+ module TestBench
2
+ class Run
3
+ module Controls
4
+ module File
5
+ def self.example(filename: nil, directory: nil, root_directory: nil)
6
+ filename ||= self.filename
7
+ directory ||= self.directory
8
+
9
+ if root_directory == :none
10
+ root_directory = nil
11
+ else
12
+ root_directory ||= self.root_directory
13
+ end
14
+
15
+ paths = []
16
+
17
+ if root_directory
18
+ paths << root_directory
19
+ end
20
+
21
+ paths << directory
22
+ paths << filename
23
+
24
+ ::File.join(*paths)
25
+ end
26
+
27
+ def self.random
28
+ Test.random
29
+ end
30
+
31
+ def self.filename
32
+ Name.example
33
+ end
34
+
35
+ def self.directory
36
+ Test.directory
37
+ end
38
+
39
+ def self.root_directory
40
+ '/some-root-dir'
41
+ end
42
+
43
+ module Random
44
+ def self.example(basename: nil, directory: nil, root_directory: nil)
45
+ basename ||= Test::Random.basename
46
+ directory ||= Test::Random.directory
47
+
48
+ filename = Name::Random.example(basename:)
49
+
50
+ File.example(filename:, directory:, root_directory:)
51
+ end
52
+ end
53
+
54
+ module Local
55
+ def self.example
56
+ File.example(root_directory: :none)
57
+ end
58
+
59
+ def self.random
60
+ File::Random.example(root_directory: :none)
61
+ end
62
+ end
63
+
64
+ module Test
65
+ extend self
66
+
67
+ def self.example(root_directory: nil)
68
+ filename = Name.example(basename:)
69
+
70
+ File.example(filename:, directory:, root_directory:)
71
+ end
72
+
73
+ def self.random
74
+ Random.example
75
+ end
76
+
77
+ def basename
78
+ 'some_test_file'
79
+ end
80
+
81
+ def directory
82
+ 'test/automated'
83
+ end
84
+
85
+ module Random
86
+ extend Test
87
+
88
+ def self.example(root_directory: nil)
89
+ filename = Name::Random.example(basename:)
90
+
91
+ File.example(filename:, directory:, root_directory:)
92
+ end
93
+ end
94
+
95
+ module Local
96
+ def self.example
97
+ Test.example(root_directory: :none)
98
+ end
99
+
100
+ def self.random
101
+ Test::Random.example(root_directory: :none)
102
+ end
103
+ end
104
+ end
105
+
106
+ module Implementation
107
+ extend self
108
+
109
+ def self.example(root_directory: nil)
110
+ filename = Name.example(basename:)
111
+
112
+ File.example(filename:, directory:, root_directory:)
113
+ end
114
+
115
+ def self.random
116
+ Random.example
117
+ end
118
+
119
+ def basename
120
+ 'some_file'
121
+ end
122
+
123
+ def directory
124
+ 'lib/some_directory'
125
+ end
126
+
127
+ module Random
128
+ extend Implementation
129
+
130
+ def self.example(root_directory: nil)
131
+ filename = Name::Random.example(basename:)
132
+
133
+ File.example(filename:, directory:, root_directory:)
134
+ end
135
+ end
136
+
137
+ module Local
138
+ def self.example
139
+ Implementation.example(root_directory: :none)
140
+ end
141
+
142
+ def self.random
143
+ Implementation::Random.example(root_directory: :none)
144
+ end
145
+ end
146
+ end
147
+
148
+ module Name
149
+ def self.example(basename: nil)
150
+ basename ||= self.basename
151
+
152
+ Telemetry::Controls::File::Name.example(basename:, extension:)
153
+ end
154
+
155
+ def self.random
156
+ Random.example
157
+ end
158
+
159
+ def self.basename
160
+ 'some_file'
161
+ end
162
+
163
+ def self.extension
164
+ '.rb'
165
+ end
166
+
167
+ module Random
168
+ def self.example(basename: nil)
169
+ basename ||= Name.basename
170
+
171
+ extension = Name.extension
172
+
173
+ Telemetry::Controls::File::Name::Random.example(basename:, extension:)
174
+ end
175
+ end
176
+ end
177
+ end
178
+ end
179
+ end
180
+ end
@@ -0,0 +1,15 @@
1
+ module TestBench
2
+ class Run
3
+ module Controls
4
+ module Path
5
+ def self.example
6
+ File.example
7
+ end
8
+
9
+ def self.random
10
+ File.random
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,7 @@
1
+ module TestBench
2
+ class Run
3
+ module Controls
4
+ ProcessID = Session::Controls::ProcessID
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ module TestBench
2
+ class Run
3
+ module Controls
4
+ Random = Fixture::Controls::Random
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ module TestBench
2
+ class Run
3
+ module Controls
4
+ Result = Fixture::Controls::Result
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ module TestBench
2
+ class Run
3
+ module Controls
4
+ Time = Session::Controls::Time
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,26 @@
1
+ require 'test_bench/fixture/controls'
2
+
3
+ require 'test_bench/run/controls/file'
4
+ require 'test_bench/run/controls/file/create'
5
+ require 'test_bench/run/controls/file/pattern'
6
+ require 'test_bench/run/controls/exception'
7
+ require 'test_bench/run/controls/process_id'
8
+ require 'test_bench/run/controls/random'
9
+ require 'test_bench/run/controls/result'
10
+ require 'test_bench/run/controls/time'
11
+
12
+ require 'test_bench/run/controls/events/event_data'
13
+ require 'test_bench/run/controls/events/file_started'
14
+ require 'test_bench/run/controls/events/file_finished'
15
+ require 'test_bench/run/controls/events/file_crashed'
16
+ require 'test_bench/run/controls/events/started'
17
+ require 'test_bench/run/controls/events/finished'
18
+
19
+ require 'test_bench/run/controls/directory'
20
+
21
+ require 'test_bench/run/controls/path'
22
+
23
+ require 'test_bench/run/controls/event_data'
24
+ require 'test_bench/run/controls/events/session'
25
+
26
+ require 'test_bench/run/controls/executor'
@@ -0,0 +1,12 @@
1
+ module TestBench
2
+ class Run
3
+ module Events
4
+ FileStarted = Telemetry::Event.define(:file)
5
+ FileFinished = Telemetry::Event.define(:file, :result)
6
+ FileCrashed = Telemetry::Event.define(:file, :error_message, :error_text)
7
+
8
+ Started = Telemetry::Event.define
9
+ Finished = Telemetry::Event.define(:result)
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,112 @@
1
+ module TestBench
2
+ class Run
3
+ module Executor
4
+ class Serial
5
+ include Executor
6
+
7
+ attr_accessor :file_reader
8
+ attr_accessor :file_writer
9
+ attr_accessor :telemetry_reader
10
+ attr_accessor :telemetry_writer
11
+
12
+ def session_store
13
+ @session_store ||= Session::Store.new
14
+ end
15
+ attr_writer :session_store
16
+
17
+ def configure
18
+ Session::Store.configure(self)
19
+ end
20
+
21
+ def start
22
+ self.file_reader, self.file_writer = ::IO.pipe(flags: ::IO::DIRECT | ::IO::SYNC)
23
+ self.telemetry_reader, self.telemetry_writer = ::IO.pipe(flags: ::IO::DIRECT | ::IO::SYNC)
24
+
25
+ fork do
26
+ file_writer.close
27
+ telemetry_reader.close
28
+
29
+ start!
30
+ end
31
+
32
+ file_reader.close
33
+ telemetry_writer.close
34
+ end
35
+
36
+ def start!
37
+ telemetry_sink = Telemetry::Sink::File.new(telemetry_writer)
38
+
39
+ session = Session.build do |telemetry|
40
+ telemetry.register(telemetry_sink)
41
+ end
42
+
43
+ session_store.reset(session)
44
+
45
+ run_file = Run::File.build(session:)
46
+
47
+ until file_reader.eof?
48
+ file = file_reader.gets(chomp: true)
49
+
50
+ run_file.(file)
51
+ end
52
+
53
+ rescue => exception
54
+
55
+ ensure
56
+ if not exception.nil?
57
+ fork { start! }
58
+
59
+ file_reader.close
60
+ telemetry_writer.close
61
+
62
+ exit(false)
63
+ end
64
+ end
65
+ # Must refer to line containing "fork { start! }"
66
+ def fork_line = __LINE__ - 9
67
+
68
+ def execute(file)
69
+ file_writer.puts(file)
70
+
71
+ session = session_store.fetch
72
+
73
+ loop do
74
+ event_text = telemetry_reader.gets
75
+
76
+ event_data = Telemetry::EventData.load(event_text)
77
+ session.telemetry.record(event_data)
78
+
79
+ if event_data.type == :FileCrashed
80
+ session.record_failure
81
+ dump_exception_text(event_data)
82
+ break
83
+ end
84
+
85
+ if event_data.type == :FileFinished
86
+ break
87
+ end
88
+ end
89
+ end
90
+
91
+ def finish
92
+ file_writer.close
93
+ telemetry_reader.close
94
+
95
+ ::Process.waitall
96
+ end
97
+
98
+ def dump_exception_text(file_crashed_data)
99
+ error_text = file_crashed_data.data.last
100
+
101
+ error_text.gsub!(backtrace_fork_pattern, '')
102
+
103
+ STDERR.write("#{error_text}\n")
104
+ end
105
+
106
+ def backtrace_fork_pattern
107
+ @backtrace_fork_pattern ||= /^.*#{__FILE__}:#{fork_line}.*?\n/m
108
+ end
109
+ end
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,45 @@
1
+ module TestBench
2
+ class Run
3
+ module Executor
4
+ module Substitute
5
+ def self.build
6
+ Executor.new
7
+ end
8
+
9
+ class Executor
10
+ include Run::Executor
11
+
12
+ def files
13
+ @files ||= []
14
+ end
15
+
16
+ attr_accessor :started
17
+ def started? = !!started
18
+
19
+ attr_accessor :finished
20
+ def finished? = !!finished
21
+
22
+ def start
23
+ self.started = true
24
+ end
25
+
26
+ def execute(file)
27
+ files << file
28
+ end
29
+
30
+ def executed?(file=nil)
31
+ if not file.nil?
32
+ files.include?(file)
33
+ else
34
+ !files.empty?
35
+ end
36
+ end
37
+
38
+ def finish
39
+ self.finished = true
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,44 @@
1
+ module TestBench
2
+ class Run
3
+ module Executor
4
+ AbstractMethodError = Class.new(RuntimeError)
5
+
6
+ def self.included(cls)
7
+ cls.class_exec do
8
+ extend Build
9
+ extend Configure
10
+ end
11
+ end
12
+
13
+ def configure
14
+ end
15
+
16
+ def start
17
+ end
18
+
19
+ def execute(file)
20
+ raise AbstractMethodError, "Subclass didn't implement execute (File: #{file.inspect})"
21
+ end
22
+
23
+ def finish
24
+ end
25
+
26
+ module Build
27
+ def build
28
+ instance = new
29
+ instance.configure
30
+ instance
31
+ end
32
+ end
33
+
34
+ module Configure
35
+ def configure(receiver, attr_name: nil)
36
+ attr_name ||= :executor
37
+
38
+ instance = build
39
+ receiver.public_send(:"#{attr_name}=", instance)
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,41 @@
1
+ module TestBench
2
+ class Run
3
+ class File
4
+ module Substitute
5
+ def self.build
6
+ File.new
7
+ end
8
+
9
+ class File
10
+ def result
11
+ @result.nil? ? @result = true : @result
12
+ end
13
+ attr_writer :result
14
+ alias :set_result :result=
15
+
16
+ attr_accessor :exception
17
+ alias :set_exception :exception=
18
+ def exception? = !!exception
19
+
20
+ def files
21
+ @files ||= []
22
+ end
23
+
24
+ def call(file)
25
+ files << file
26
+
27
+ if exception?
28
+ raise exception
29
+ end
30
+
31
+ result
32
+ end
33
+
34
+ def file?(file)
35
+ files.include?(file)
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,73 @@
1
+ module TestBench
2
+ class Run
3
+ class File
4
+ ReadFileError = Class.new(RuntimeError)
5
+
6
+ def root_directory
7
+ @root_directory ||= ::Dir.pwd
8
+ end
9
+ attr_writer :root_directory
10
+
11
+ def session
12
+ @session ||= Session::Substitute.build
13
+ end
14
+ attr_writer :session
15
+
16
+ def self.build(session: nil)
17
+ instance = new
18
+ Session.configure(instance, session:)
19
+ instance
20
+ end
21
+
22
+ def call(file)
23
+ begin
24
+ source = ::File.read(file)
25
+ rescue SystemCallError => error
26
+ raise ReadFileError, "Couldn't run #{file}: #{error.message.partition(' @ ').first}"
27
+ end
28
+
29
+ failure_sequence = session.failure_sequence
30
+
31
+ session.record_event(Events::FileStarted.new(file))
32
+
33
+ begin
34
+ TOPLEVEL_BINDING.eval(source, file)
35
+
36
+ rescue => exception
37
+ raise exception
38
+
39
+ ensure
40
+ if not exception.nil?
41
+ error_message = error_message(exception)
42
+ error_text = error_text(exception)
43
+
44
+ session.record_event(Events::FileCrashed.new(file, error_message, error_text))
45
+
46
+ raise exception
47
+ end
48
+ end
49
+
50
+ failed = session.failed?(failure_sequence)
51
+ result = !failed
52
+
53
+ session.record_event(Events::FileFinished.new(file, result))
54
+
55
+ result
56
+ end
57
+
58
+ def error_message(exception)
59
+ error_message = exception.full_message(order: :top, highlight: false).each_line(chomp: true).first
60
+
61
+ if error_message.delete_prefix!(root_directory)
62
+ error_message.delete_prefix!('/')
63
+ end
64
+
65
+ error_message
66
+ end
67
+
68
+ def error_text(exception)
69
+ exception.full_message
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,46 @@
1
+ module TestBench
2
+ class Run
3
+ class GetFiles
4
+ module Substitute
5
+ def self.build
6
+ GetFiles.new
7
+ end
8
+
9
+ class GetFiles
10
+ attr_accessor :file_error
11
+ def file_error? = !!file_error
12
+
13
+ def files
14
+ @files ||= []
15
+ end
16
+ attr_writer :files
17
+ alias :set_files :files=
18
+
19
+ def paths
20
+ @paths ||= []
21
+ end
22
+
23
+ def file_error!
24
+ self.file_error = true
25
+ end
26
+
27
+ def call(path, *paths, &block)
28
+ [path, *paths].each do |path|
29
+ self.paths << path
30
+ end
31
+
32
+ if file_error?
33
+ raise FileError
34
+ end
35
+
36
+ files.each(&block)
37
+ end
38
+
39
+ def path?(path)
40
+ paths.include?(path)
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,72 @@
1
+ module TestBench
2
+ class Run
3
+ class GetFiles
4
+ FileError = Class.new(RuntimeError)
5
+
6
+ def exclude_file_pattern
7
+ @exclude_file_pattern ||= Defaults.exclude_file_pattern
8
+ end
9
+ attr_writer :exclude_file_pattern
10
+
11
+ def self.build(exclude_file_pattern: nil)
12
+ instance = new
13
+
14
+ if not exclude_file_pattern.nil?
15
+ instance.exclude_file_pattern = exclude_file_pattern
16
+ end
17
+
18
+ instance
19
+ end
20
+
21
+ def self.call(path, *paths, exclude_file_pattern: nil, &block)
22
+ instance = build(exclude_file_pattern:)
23
+ instance.(path, *paths, &block)
24
+ end
25
+
26
+ def self.configure(receiver, exclude_file_pattern: nil, attr_name: nil)
27
+ attr_name ||= :get_files
28
+
29
+ instance = build(exclude_file_pattern:)
30
+ receiver.public_send(:"#{attr_name}=", instance)
31
+ end
32
+
33
+ def call(path, *paths, &block)
34
+ paths = [path, *paths]
35
+
36
+ exclude_file_pattern = ::File.join('**', self.exclude_file_pattern)
37
+
38
+ fnmatch_flags = ::File::FNM_PATHNAME | ::File::FNM_EXTGLOB | ::File::FNM_SYSCASE
39
+
40
+ paths.each do |path|
41
+ if ::File.extname(path).empty?
42
+ pattern = ::File.join(path, '**/*.rb')
43
+ else
44
+ pattern = path
45
+ end
46
+
47
+ assure_extant(path)
48
+
49
+ Dir.glob(pattern).each do |file|
50
+ if ::File.fnmatch?(exclude_file_pattern, file, fnmatch_flags)
51
+ next
52
+ end
53
+
54
+ block.(file)
55
+ end
56
+ end
57
+ end
58
+
59
+ def assure_extant(path)
60
+ ::File.stat(path)
61
+ rescue Errno::ENOENT => enoent
62
+ raise FileError, enoent.message
63
+ end
64
+
65
+ module Defaults
66
+ def self.exclude_file_pattern
67
+ ENV.fetch('TEST_BENCH_EXCLUDE_FILE_PATTERN', '*_init.rb')
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end