turbo_test 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 7cfabcac4630bd19d2529fa63dc7022e4c7dddf518afe98284a838c10ec0e0cf
4
+ data.tar.gz: 23941350f74476d097e5e4af2c65d877265c6a6b5b32911f68b6b8aae76230a8
5
+ SHA512:
6
+ metadata.gz: 5430e363ce617c8d0581a6c2cceed6477c63eed7234636ed031a97447ff9f512d53d269be17df4ff9e9a139493cd67d41da2f731a659f59838ddba98bd8396dd
7
+ data.tar.gz: 316141c95de82103149a2d57e532e08f252797e8f4953c2493020906b77cf8c252d20252dcfaccea1df1c9145407848611db0c358ab5baa430d867cf42d115d7
@@ -0,0 +1,32 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Copyright, 2020, by Samuel G. D. Williams. <http://www.codeotaku.com>
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in
13
+ # all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ # THE SOFTWARE.
22
+
23
+ require_relative '../lib/turbo_test/command'
24
+
25
+ begin
26
+ TurboTest::Command.call
27
+ rescue Interrupt
28
+ # Ignore.
29
+ rescue => error
30
+ Async.logger.error(TurboTest::Command) {error}
31
+ exit! 1
32
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright, 2020, by Samuel G. D. Williams. <http://www.codeotaku.com>
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in
13
+ # all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ # THE SOFTWARE.
22
+
23
+ require_relative 'turbo_test/version'
24
+
25
+ require_relative 'turbo_test/server'
26
+ require_relative 'turbo_test/rspec/job'
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright, 2020, by Samuel G. D. Williams. <http://www.codeotaku.com>
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in
13
+ # all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ # THE SOFTWARE.
22
+
23
+ require_relative 'command/top'
24
+
25
+ module TurboTest
26
+ module Command
27
+ # The main entry point for the `falcon` executable.
28
+ # @parameter arguments [Array(String)] The command line arguments.
29
+ def self.call(*arguments)
30
+ Top.call(*arguments)
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright, 2020, by Samuel G. D. Williams. <http://www.codeotaku.com>
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in
13
+ # all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ # THE SOFTWARE.
22
+
23
+ require 'samovar'
24
+
25
+ require_relative '../server'
26
+ require_relative '../configuration'
27
+ require_relative '../rspec/job'
28
+
29
+ require 'bundler'
30
+
31
+ module TurboTest
32
+ module Command
33
+ class Run < Samovar::Command
34
+ self.description = "Runs tests using a distributed fan-out queue."
35
+
36
+ # The command line options.
37
+ # @attribute [Samovar::Options]
38
+ options do
39
+ option '-n/--count <count>', "Number of instances to start.", default: Async::Container.processor_count, type: Integer
40
+
41
+ option '-c/--configuration', "The configuration path to use.", default: "turbo_test.rb"
42
+ end
43
+
44
+ many :paths, "The test paths to execute."
45
+
46
+ # Prepare the environment and run the controller.
47
+ def call
48
+ Async.logger.info(self) do |buffer|
49
+ buffer.puts "TurboTest v#{VERSION} preparing for maximum thrust!"
50
+ end
51
+
52
+ path = @options[:configuration]
53
+ full_path = File.expand_path(path)
54
+
55
+ if File.exist?(full_path)
56
+ configuration = Configuration.load(full_path)
57
+ end
58
+
59
+ Bundler.require(:preload)
60
+
61
+ if GC.respond_to?(:compact)
62
+ GC.compact
63
+ end
64
+
65
+ server = Server.new(configuration)
66
+
67
+ queue = paths.map do |path|
68
+ [RSpec::Job, path]
69
+ end
70
+
71
+ results = server.host(queue)
72
+
73
+ server.workers
74
+ server.wait
75
+
76
+ return results.read
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright, 2020, by Samuel G. D. Williams. <http://www.codeotaku.com>
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in
13
+ # all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ # THE SOFTWARE.
22
+
23
+ require_relative 'run'
24
+ require_relative '../version'
25
+
26
+ require 'samovar'
27
+
28
+ module TurboTest
29
+ module Command
30
+ # The top level command for the `falcon` executable.
31
+ class Top < Samovar::Command
32
+ self.description = "A parallel test runner."
33
+
34
+ # The command line options.
35
+ # @attribute [Samovar::Options]
36
+ options do
37
+ option '-h/--help', "Print out help information."
38
+ option '-v/--version', "Print out the application version."
39
+ end
40
+
41
+ # The nested command to execute.
42
+ # @name nested
43
+ # @attribute [Command]
44
+ nested :command, {
45
+ 'run' => Run,
46
+ }, default: 'run'
47
+
48
+ # Prepare the environment and invoke the sub-command.
49
+ def call
50
+ if @options[:version]
51
+ puts "#{self.name} v#{VERSION}"
52
+ elsif @options[:help]
53
+ self.print_usage
54
+ else
55
+ @command.call
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright, 2020, by Samuel G. D. Williams. <http://www.codeotaku.com>
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in
13
+ # all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ # THE SOFTWARE.
22
+
23
+ module TurboTest
24
+ class Configuration
25
+ def initialize
26
+ @worker = nil
27
+ end
28
+
29
+ attr_accessor :worker
30
+
31
+ def self.load(path)
32
+ configuration = self.new
33
+
34
+ loader = Loader.new(configuration)
35
+ loader.instance_eval(File.read(path), path.to_s)
36
+
37
+ return configuration
38
+ end
39
+
40
+ class Loader
41
+ def initialize(configuration)
42
+ @configuration = configuration
43
+ end
44
+
45
+ def worker(&block)
46
+ @configuration.worker = block
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright, 2020, by Samuel G. D. Williams. <http://www.codeotaku.com>
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in
13
+ # all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ # THE SOFTWARE.
22
+
23
+ require 'rspec/support'
24
+ require 'rspec/core/formatters'
25
+
26
+ module TurboTest
27
+ module RSpec
28
+ class ExampleFormatter
29
+ ::RSpec::Core::Formatters.register self, :example_finished, :example_failed, :dump_summary
30
+
31
+ def initialize(packer)
32
+ @packer = packer
33
+
34
+ @colorizer = ::RSpec::Core::Formatters::ConsoleCodes
35
+ end
36
+
37
+ def output
38
+ @packer
39
+ end
40
+
41
+ def example_finished(notification)
42
+ @packer.write([:finished, notification.example.id])
43
+ @packer.flush
44
+ end
45
+
46
+ def example_failed(notification)
47
+ example = notification.example
48
+
49
+ presenter = ::RSpec::Core::Formatters::ExceptionPresenter.new(example.exception, example)
50
+
51
+ message = {
52
+ description: example.full_description,
53
+ location: example.location_rerun_argument,
54
+ report: presenter.fully_formatted(nil, @colorizer),
55
+ }
56
+
57
+ @packer.write([:failed, message])
58
+ @packer.flush
59
+ end
60
+
61
+ def dump_summary(summary)
62
+ count = summary.examples.count
63
+
64
+ @packer.write([:count, count])
65
+ @packer.flush
66
+ end
67
+ end
68
+ end
69
+ end
70
+
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright, 2020, by Samuel G. D. Williams. <http://www.codeotaku.com>
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in
13
+ # all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ # THE SOFTWARE.
22
+
23
+ require 'rspec/core'
24
+
25
+ require_relative 'example_formatter'
26
+
27
+ module TurboTest
28
+ module RSpec
29
+ class ConfigurationOptions < ::RSpec::Core::ConfigurationOptions
30
+ def initialize(arguments, packer:)
31
+ super(arguments)
32
+
33
+ @packer = packer
34
+ end
35
+
36
+ def configure(config)
37
+ super(config)
38
+
39
+ config.add_formatter(ExampleFormatter.new(@packer))
40
+ end
41
+
42
+ def load_formatters_into(config)
43
+ # Don't load any formatters, default or otherwise.
44
+ end
45
+ end
46
+
47
+ class Job
48
+ def initialize(path)
49
+ @path = path
50
+ end
51
+
52
+ def call(packer:, stdout: $stdout, stderr: $stderr)
53
+ reset_rspec_state!
54
+
55
+ options = ConfigurationOptions.new([@path],
56
+ packer: packer
57
+ )
58
+
59
+ runner = ::RSpec::Core::Runner.new(options)
60
+
61
+ runner.run(stdout, stderr)
62
+ end
63
+
64
+ private
65
+
66
+ def reset_rspec_state!
67
+ ::RSpec.clear_examples
68
+
69
+ # see https://github.com/rspec/rspec-core/pull/2723
70
+ if Gem::Version.new(::RSpec::Core::Version::STRING) <= Gem::Version.new("3.9.1")
71
+ ::RSpec.world.instance_variable_set(
72
+ :@example_group_counts_by_spec_file, Hash.new(0)
73
+ )
74
+ end
75
+
76
+ # RSpec.clear_examples does not reset those, which causes issues when
77
+ # a non-example error occurs (subsequent jobs are not executed)
78
+ # TODO: upstream
79
+ ::RSpec.world.non_example_failure = false
80
+
81
+ # we don't want an error that occured outside of the examples (which
82
+ # would set this to `true`) to stop the worker
83
+ ::RSpec.world.wants_to_quit = false
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,203 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright, 2020, by Samuel G. D. Williams. <http://www.codeotaku.com>
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in
13
+ # all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ # THE SOFTWARE.
22
+
23
+ require 'async'
24
+ require 'async/container'
25
+ require 'async/io/unix_endpoint'
26
+ require 'async/io/shared_endpoint'
27
+ require 'msgpack'
28
+
29
+ module TurboTest
30
+ class Wrapper < MessagePack::Factory
31
+ def initialize
32
+ super()
33
+
34
+ # self.register_type(0x00, Object, packer: @bus.method(:temporary), unpacker: @bus.method(:[]))
35
+
36
+ self.register_type(0x01, Symbol)
37
+ self.register_type(0x02, Exception,
38
+ packer: ->(exception){Marshal.dump(exception)},
39
+ unpacker: ->(data){Marshal.load(data)},
40
+ )
41
+
42
+ self.register_type(0x03, Class,
43
+ packer: ->(klass){Marshal.dump(klass)},
44
+ unpacker: ->(data){Marshal.load(data)},
45
+ )
46
+ end
47
+ end
48
+
49
+ class Server
50
+ def initialize(configuration, endpoint = nil)
51
+ @configuration = configuration
52
+ @endpoint = endpoint || Async::IO::Endpoint.unix('turbo_test.ipc')
53
+ @wrapper = Wrapper.new
54
+
55
+ @container = Async::Container.new
56
+
57
+ @bound_endpoint = Sync do
58
+ Async::IO::SharedEndpoint.bound(@endpoint)
59
+ end
60
+ end
61
+
62
+ def host(queue)
63
+ input, output = IO.pipe
64
+
65
+ @container.spawn(name: "#{self.class} Host") do |instance|
66
+ connected = 0
67
+ progress = Console.logger.progress("Queue", queue.size)
68
+ failures = []
69
+
70
+ statistics = {
71
+ succeeded: 0,
72
+ failed: 0,
73
+ }
74
+
75
+ Async do |task|
76
+ instance.ready!
77
+
78
+ @bound_endpoint.accept do |peer|
79
+ # Console.logger.info(self) {"Incoming connection from #{peer}..."}
80
+
81
+ packer = @wrapper.packer(peer)
82
+ unpacker = @wrapper.unpacker(peer)
83
+
84
+ packer.write([:connected, connected])
85
+ connected += 1
86
+
87
+ unpacker.each do |message|
88
+ command, *arguments = message
89
+
90
+ case command
91
+ when :ready
92
+ Console.logger.debug("Child Ready") {arguments}
93
+
94
+ if job = queue.pop
95
+ packer.write([:job, job])
96
+ packer.flush
97
+ else
98
+ Console.logger.debug("Child Closed")
99
+ peer.close_write
100
+ connected -= 1
101
+
102
+ if connected.zero?
103
+ print_summary(failures)
104
+ task.stop
105
+ end
106
+ end
107
+ when :finished
108
+ Console.logger.debug("Job Finished") {arguments}
109
+ when :failed
110
+ Console.logger.debug("Job Failed") {arguments}
111
+ failures << arguments
112
+ statistics[:failed] += 1
113
+ when :count
114
+ Console.logger.debug("Job Count") {arguments}
115
+ statistics[:succeeded] += arguments.first
116
+ when :result
117
+ Console.logger.debug("Job Result") {arguments}
118
+ progress.increment
119
+ when :error
120
+ Console.logger.error("Job Error") {arguments}
121
+ else
122
+ Console.logger.warn(self) {"Unhandled command: #{command}#{arguments.inspect}"}
123
+ end
124
+ end
125
+ end
126
+ end
127
+
128
+ ensure
129
+ Console.logger.info("Writing results")
130
+ @wrapper.packer(output).write(statistics).flush
131
+ end
132
+
133
+ output.close
134
+
135
+ return @wrapper.unpacker(input)
136
+ end
137
+
138
+ def print_summary(failures, command = $0)
139
+ return unless failures.any?
140
+
141
+ failures.sort_by!{|(failure)| failure[:location]}
142
+
143
+ $stderr.puts nil, "Failures:", nil
144
+
145
+ failures.each do |(failure)|
146
+ $stderr.puts failure[:report]
147
+ $stderr.puts
148
+ end
149
+
150
+ $stderr.puts nil, "Summary:", nil
151
+
152
+ failures.each do |(failure)|
153
+ $stderr.puts "#{command} #{failure[:location]} \# #{failure[:description]}"
154
+ end
155
+
156
+ $stderr.puts
157
+ end
158
+
159
+ def workers
160
+ @container.run(name: "#{self.class} Worker") do |instance|
161
+ Async do |task|
162
+ @endpoint.connect do |peer|
163
+ instance.ready!
164
+
165
+ packer = @wrapper.packer(peer)
166
+ unpacker = @wrapper.unpacker(peer)
167
+
168
+ packer.write([:ready])
169
+ packer.flush
170
+
171
+ unpacker.each do |message|
172
+ command, tail = message
173
+
174
+ case command
175
+ when :connected
176
+ @configuration&.worker&.call(*tail)
177
+ when :job
178
+ klass, *arguments = *tail
179
+
180
+ begin
181
+ result = klass.new(*arguments).call(packer: packer)
182
+ packer.write([:result, result])
183
+ rescue Exception => exception
184
+ packer.write([:error, exception, exception.backtrace])
185
+ end
186
+
187
+ packer.write([:ready])
188
+ packer.flush
189
+ else
190
+ Console.logger.warn(self) {"Unhandled command: #{command}#{arguments.inspect}"}
191
+ end
192
+ end
193
+ end
194
+ end
195
+ end
196
+ end
197
+
198
+ def wait
199
+ @container.wait
200
+ @bound_endpoint.close
201
+ end
202
+ end
203
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright, 2020, by Samuel G. D. Williams. <http://www.codeotaku.com>
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in
13
+ # all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ # THE SOFTWARE.
22
+
23
+ module TurboTest
24
+ VERSION = "0.1.0"
25
+ end
metadata ADDED
@@ -0,0 +1,124 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: turbo_test
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Samuel Williams
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-11-01 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: async-container
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: async-io
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: msgpack
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: samovar
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description:
84
+ email:
85
+ executables:
86
+ - turbo_test
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - bin/turbo_test
91
+ - lib/turbo_test.rb
92
+ - lib/turbo_test/command.rb
93
+ - lib/turbo_test/command/run.rb
94
+ - lib/turbo_test/command/top.rb
95
+ - lib/turbo_test/configuration.rb
96
+ - lib/turbo_test/rspec/example_formatter.rb
97
+ - lib/turbo_test/rspec/job.rb
98
+ - lib/turbo_test/server.rb
99
+ - lib/turbo_test/version.rb
100
+ homepage: https://github.com/ioquatix/turbo_test
101
+ licenses:
102
+ - MIT
103
+ metadata:
104
+ funding_uri: https://github.com/sponsors/ioquatix/
105
+ post_install_message:
106
+ rdoc_options: []
107
+ require_paths:
108
+ - lib
109
+ required_ruby_version: !ruby/object:Gem::Requirement
110
+ requirements:
111
+ - - ">="
112
+ - !ruby/object:Gem::Version
113
+ version: 2.3.0
114
+ required_rubygems_version: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - ">="
117
+ - !ruby/object:Gem::Version
118
+ version: '0'
119
+ requirements: []
120
+ rubygems_version: 3.0.3
121
+ signing_key:
122
+ specification_version: 4
123
+ summary: Press the turbo button... for your tests.
124
+ test_files: []