test_bench-run 0

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