mutant 0.11.28 → 0.11.29
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/mutant/ast.rb +8 -9
- data/lib/mutant/bootstrap.rb +39 -13
- data/lib/mutant/cli/command/environment/test.rb +38 -1
- data/lib/mutant/cli/command/environment.rb +11 -0
- data/lib/mutant/context.rb +31 -61
- data/lib/mutant/env.rb +8 -1
- data/lib/mutant/expression/method.rb +4 -1
- data/lib/mutant/expression/methods.rb +4 -1
- data/lib/mutant/expression/namespace.rb +4 -4
- data/lib/mutant/hooks.rb +1 -0
- data/lib/mutant/integration/null.rb +1 -0
- data/lib/mutant/integration.rb +5 -1
- data/lib/mutant/matcher/descendants.rb +1 -1
- data/lib/mutant/matcher/method/instance.rb +5 -4
- data/lib/mutant/matcher/method/metaclass.rb +1 -1
- data/lib/mutant/matcher/method/singleton.rb +1 -1
- data/lib/mutant/matcher/method.rb +30 -4
- data/lib/mutant/matcher/methods.rb +8 -7
- data/lib/mutant/matcher/namespace.rb +1 -1
- data/lib/mutant/meta/example.rb +12 -2
- data/lib/mutant/mutation/runner/sink.rb +7 -3
- data/lib/mutant/mutation/runner.rb +2 -3
- data/lib/mutant/parallel/connection.rb +178 -0
- data/lib/mutant/parallel/pipe.rb +39 -0
- data/lib/mutant/parallel/worker.rb +42 -14
- data/lib/mutant/parallel.rb +18 -7
- data/lib/mutant/reporter/cli/format.rb +19 -2
- data/lib/mutant/reporter/cli/printer/test.rb +138 -0
- data/lib/mutant/reporter/cli.rb +33 -4
- data/lib/mutant/reporter.rb +22 -1
- data/lib/mutant/result.rb +53 -2
- data/lib/mutant/scope.rb +41 -1
- data/lib/mutant/subject/method/instance.rb +3 -2
- data/lib/mutant/subject/method/metaclass.rb +1 -1
- data/lib/mutant/subject/method/singleton.rb +2 -2
- data/lib/mutant/subject/method.rb +1 -1
- data/lib/mutant/test/runner/sink.rb +51 -0
- data/lib/mutant/test/runner.rb +62 -0
- data/lib/mutant/timer.rb +9 -0
- data/lib/mutant/version.rb +1 -1
- data/lib/mutant/world.rb +2 -0
- data/lib/mutant.rb +7 -1
- metadata +9 -19
- data/lib/mutant/pipe.rb +0 -96
data/lib/mutant/reporter/cli.rb
CHANGED
@@ -4,7 +4,7 @@ module Mutant
|
|
4
4
|
class Reporter
|
5
5
|
# Reporter that reports in human readable format
|
6
6
|
class CLI < self
|
7
|
-
include Anima.new(:output, :format)
|
7
|
+
include Anima.new(:print_warnings, :output, :format)
|
8
8
|
|
9
9
|
# Build reporter
|
10
10
|
#
|
@@ -13,8 +13,9 @@ module Mutant
|
|
13
13
|
# @return [Reporter::CLI]
|
14
14
|
def self.build(output)
|
15
15
|
new(
|
16
|
-
format:
|
17
|
-
|
16
|
+
format: Format::Progressive.new(tty: output.respond_to?(:tty?) && output.tty?),
|
17
|
+
print_warnings: false,
|
18
|
+
output: output
|
18
19
|
)
|
19
20
|
end
|
20
21
|
|
@@ -28,6 +29,16 @@ module Mutant
|
|
28
29
|
self
|
29
30
|
end
|
30
31
|
|
32
|
+
# Report test start
|
33
|
+
#
|
34
|
+
# @param [Env] env
|
35
|
+
#
|
36
|
+
# @return [self]
|
37
|
+
def test_start(env)
|
38
|
+
write(format.test_start(env))
|
39
|
+
self
|
40
|
+
end
|
41
|
+
|
31
42
|
# Report progress object
|
32
43
|
#
|
33
44
|
# @param [Parallel::Status] status
|
@@ -38,6 +49,14 @@ module Mutant
|
|
38
49
|
self
|
39
50
|
end
|
40
51
|
|
52
|
+
# Report progress object
|
53
|
+
#
|
54
|
+
# @return [self]
|
55
|
+
def test_progress(status)
|
56
|
+
write(format.test_progress(status))
|
57
|
+
self
|
58
|
+
end
|
59
|
+
|
41
60
|
# Report delay in seconds
|
42
61
|
#
|
43
62
|
# @return [Float]
|
@@ -51,7 +70,7 @@ module Mutant
|
|
51
70
|
#
|
52
71
|
# @return [self]
|
53
72
|
def warn(message)
|
54
|
-
output.puts(message)
|
73
|
+
output.puts(message) if print_warnings
|
55
74
|
self
|
56
75
|
end
|
57
76
|
|
@@ -65,6 +84,16 @@ module Mutant
|
|
65
84
|
self
|
66
85
|
end
|
67
86
|
|
87
|
+
# Report env
|
88
|
+
#
|
89
|
+
# @param [Result::Env] env
|
90
|
+
#
|
91
|
+
# @return [self]
|
92
|
+
def test_report(env)
|
93
|
+
Printer::Test::EnvResult.call(output: output, object: env)
|
94
|
+
self
|
95
|
+
end
|
96
|
+
|
68
97
|
private
|
69
98
|
|
70
99
|
def write(frame)
|
data/lib/mutant/reporter.rb
CHANGED
@@ -19,13 +19,27 @@ module Mutant
|
|
19
19
|
# @return [self]
|
20
20
|
abstract_method :start
|
21
21
|
|
22
|
-
# Report
|
22
|
+
# Report test start
|
23
|
+
#
|
24
|
+
# @param [Env] env
|
25
|
+
#
|
26
|
+
# @return [self]
|
27
|
+
abstract_method :test_start
|
28
|
+
|
29
|
+
# Report final state
|
23
30
|
#
|
24
31
|
# @param [Runner::Collector] collector
|
25
32
|
#
|
26
33
|
# @return [self]
|
27
34
|
abstract_method :report
|
28
35
|
|
36
|
+
# Report final test state
|
37
|
+
#
|
38
|
+
# @param [Runner::Collector] collector
|
39
|
+
#
|
40
|
+
# @return [self]
|
41
|
+
abstract_method :test_report
|
42
|
+
|
29
43
|
# Report progress on object
|
30
44
|
#
|
31
45
|
# @param [Object] object
|
@@ -33,6 +47,13 @@ module Mutant
|
|
33
47
|
# @return [self]
|
34
48
|
abstract_method :progress
|
35
49
|
|
50
|
+
# Report progress on object
|
51
|
+
#
|
52
|
+
# @param [Object] object
|
53
|
+
#
|
54
|
+
# @return [self]
|
55
|
+
abstract_method :test_progress
|
56
|
+
|
36
57
|
# The reporter delay
|
37
58
|
#
|
38
59
|
# @return [Float]
|
data/lib/mutant/result.rb
CHANGED
@@ -105,12 +105,62 @@ module Mutant
|
|
105
105
|
def stop?
|
106
106
|
env.config.fail_fast && !subject_results.all?(&:success?)
|
107
107
|
end
|
108
|
-
|
109
108
|
end # Env
|
110
109
|
|
110
|
+
# TestEnv result object
|
111
|
+
class TestEnv
|
112
|
+
include Result, Anima.new(
|
113
|
+
:env,
|
114
|
+
:runtime,
|
115
|
+
:test_results
|
116
|
+
)
|
117
|
+
|
118
|
+
# Test if run is successful
|
119
|
+
#
|
120
|
+
# @return [Boolean]
|
121
|
+
def success?
|
122
|
+
amount_tests_failed.equal?(0)
|
123
|
+
end
|
124
|
+
memoize :success?
|
125
|
+
|
126
|
+
# Failed subject results
|
127
|
+
#
|
128
|
+
# @return [Array<Result::Test>]
|
129
|
+
def failed_test_results
|
130
|
+
test_results.reject(&:success?)
|
131
|
+
end
|
132
|
+
memoize :failed_test_results
|
133
|
+
|
134
|
+
def stop?
|
135
|
+
env.config.fail_fast && !test_results.all?(&:success?)
|
136
|
+
end
|
137
|
+
|
138
|
+
def testtime
|
139
|
+
test_results.map(&:runtime).sum(0.0)
|
140
|
+
end
|
141
|
+
|
142
|
+
def amount_tests
|
143
|
+
env.integration.all_tests.length
|
144
|
+
end
|
145
|
+
|
146
|
+
def amount_test_results
|
147
|
+
test_results.length
|
148
|
+
end
|
149
|
+
|
150
|
+
def amount_tests_failed
|
151
|
+
failed_test_results.length
|
152
|
+
end
|
153
|
+
|
154
|
+
def amount_tests_success
|
155
|
+
test_results.count(&:passed)
|
156
|
+
end
|
157
|
+
end # TestEnv
|
158
|
+
|
111
159
|
# Test result
|
112
160
|
class Test
|
113
|
-
include Anima.new(:passed, :runtime)
|
161
|
+
include Anima.new(:passed, :runtime, :output)
|
162
|
+
|
163
|
+
alias_method :success?, :passed
|
114
164
|
|
115
165
|
class VoidValue < self
|
116
166
|
include Singleton
|
@@ -120,6 +170,7 @@ module Mutant
|
|
120
170
|
# @return [undefined]
|
121
171
|
def initialize
|
122
172
|
super(
|
173
|
+
output: '',
|
123
174
|
passed: false,
|
124
175
|
runtime: 0.0
|
125
176
|
)
|
data/lib/mutant/scope.rb
CHANGED
@@ -3,6 +3,46 @@
|
|
3
3
|
module Mutant
|
4
4
|
# Class or Module bound to an exact expression
|
5
5
|
class Scope
|
6
|
-
include Anima.new(:raw, :expression)
|
6
|
+
include Adamantium, Anima.new(:raw, :expression)
|
7
|
+
|
8
|
+
NAMESPACE_DELIMITER = '::'
|
9
|
+
|
10
|
+
# Nesting of scope
|
11
|
+
#
|
12
|
+
# @return [Enumerable<Class,Module>]
|
13
|
+
def nesting
|
14
|
+
const = Object
|
15
|
+
name_nesting.map do |name|
|
16
|
+
const = const.const_get(name)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
memoize :nesting
|
20
|
+
|
21
|
+
# Unqualified name of scope
|
22
|
+
#
|
23
|
+
# @return [String]
|
24
|
+
def unqualified_name
|
25
|
+
name_nesting.last
|
26
|
+
end
|
27
|
+
|
28
|
+
# Match expressions for scope
|
29
|
+
#
|
30
|
+
# @return [Enumerable<Expression>]
|
31
|
+
def match_expressions
|
32
|
+
name_nesting.each_index.reverse_each.map do |index|
|
33
|
+
Expression::Namespace::Recursive.new(
|
34
|
+
scope_name: name_nesting.take(index.succ).join(NAMESPACE_DELIMITER)
|
35
|
+
)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
memoize :match_expressions
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def name_nesting
|
43
|
+
raw.name.split(NAMESPACE_DELIMITER)
|
44
|
+
end
|
45
|
+
memoize :name_nesting
|
46
|
+
|
7
47
|
end # Scope
|
8
48
|
end # Mutant
|
@@ -13,12 +13,12 @@ module Mutant
|
|
13
13
|
#
|
14
14
|
# @return [self]
|
15
15
|
def prepare
|
16
|
-
scope.undef_method(name)
|
16
|
+
scope.raw.undef_method(name)
|
17
17
|
self
|
18
18
|
end
|
19
19
|
|
20
20
|
def post_insert
|
21
|
-
scope.__send__(visibility, name)
|
21
|
+
scope.raw.__send__(visibility, name)
|
22
22
|
self
|
23
23
|
end
|
24
24
|
|
@@ -31,6 +31,7 @@ module Mutant
|
|
31
31
|
# @return [self]
|
32
32
|
def prepare
|
33
33
|
scope
|
34
|
+
.raw
|
34
35
|
.instance_variable_get(:@memoized_methods)
|
35
36
|
.delete(name)
|
36
37
|
|
@@ -13,12 +13,12 @@ module Mutant
|
|
13
13
|
#
|
14
14
|
# @return [self]
|
15
15
|
def prepare
|
16
|
-
scope.singleton_class.
|
16
|
+
scope.raw.singleton_class.undef_method(name)
|
17
17
|
self
|
18
18
|
end
|
19
19
|
|
20
20
|
def post_insert
|
21
|
-
scope.singleton_class.__send__(visibility, name)
|
21
|
+
scope.raw.singleton_class.__send__(visibility, name)
|
22
22
|
self
|
23
23
|
end
|
24
24
|
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Mutant
|
4
|
+
class Test
|
5
|
+
module Runner
|
6
|
+
class Sink
|
7
|
+
include Anima.new(:env)
|
8
|
+
|
9
|
+
# Initialize object
|
10
|
+
#
|
11
|
+
# @return [undefined]
|
12
|
+
def initialize(*)
|
13
|
+
super
|
14
|
+
@start = env.world.timer.now
|
15
|
+
@test_results = []
|
16
|
+
end
|
17
|
+
|
18
|
+
# Runner status
|
19
|
+
#
|
20
|
+
# @return [Result::Env]
|
21
|
+
def status
|
22
|
+
Result::TestEnv.new(
|
23
|
+
env: env,
|
24
|
+
runtime: env.world.timer.now - @start,
|
25
|
+
test_results: @test_results
|
26
|
+
)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Test if scheduling stopped
|
30
|
+
#
|
31
|
+
# @return [Boolean]
|
32
|
+
def stop?
|
33
|
+
status.stop?
|
34
|
+
end
|
35
|
+
|
36
|
+
# Handle mutation finish
|
37
|
+
#
|
38
|
+
# @return [self]
|
39
|
+
def response(response)
|
40
|
+
if response.error
|
41
|
+
env.world.stderr.puts(response.log)
|
42
|
+
fail response.error
|
43
|
+
end
|
44
|
+
|
45
|
+
@test_results << response.result.with(output: response.log)
|
46
|
+
self
|
47
|
+
end
|
48
|
+
end # Sink
|
49
|
+
end # Runner
|
50
|
+
end # Test
|
51
|
+
end # Mutant
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Mutant
|
4
|
+
class Test
|
5
|
+
module Runner
|
6
|
+
# Run against env
|
7
|
+
#
|
8
|
+
# @return [Either<String, Result>]
|
9
|
+
def self.call(env)
|
10
|
+
reporter(env).test_start(env)
|
11
|
+
|
12
|
+
Either::Right.new(run_tests(env))
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.run_tests(env)
|
16
|
+
reporter = reporter(env)
|
17
|
+
|
18
|
+
env
|
19
|
+
.record(:tests) { run_driver(reporter, async_driver(env)) }
|
20
|
+
.tap { |result| env.record(:report) { reporter.test_report(result) } }
|
21
|
+
end
|
22
|
+
private_class_method :run_tests
|
23
|
+
|
24
|
+
def self.async_driver(env)
|
25
|
+
Parallel.async(world: env.world, config: test_config(env))
|
26
|
+
end
|
27
|
+
private_class_method :async_driver
|
28
|
+
|
29
|
+
def self.run_driver(reporter, driver)
|
30
|
+
Signal.trap('INT') do
|
31
|
+
driver.stop
|
32
|
+
end
|
33
|
+
|
34
|
+
loop do
|
35
|
+
status = driver.wait_timeout(reporter.delay)
|
36
|
+
break status.payload if status.done?
|
37
|
+
reporter.test_progress(status)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
private_class_method :run_driver
|
41
|
+
|
42
|
+
def self.test_config(env)
|
43
|
+
Parallel::Config.new(
|
44
|
+
block: env.method(:run_test_index),
|
45
|
+
jobs: env.config.jobs,
|
46
|
+
on_process_start: env.method(:emit_test_worker_process_start),
|
47
|
+
process_name: 'mutant-test-runner-process',
|
48
|
+
sink: Sink.new(env: env),
|
49
|
+
source: Parallel::Source::Array.new(jobs: env.integration.all_tests.each_index.to_a),
|
50
|
+
thread_name: 'mutant-test-runner-thread',
|
51
|
+
timeout: nil
|
52
|
+
)
|
53
|
+
end
|
54
|
+
private_class_method :test_config
|
55
|
+
|
56
|
+
def self.reporter(env)
|
57
|
+
env.config.reporter
|
58
|
+
end
|
59
|
+
private_class_method :reporter
|
60
|
+
end # Runner
|
61
|
+
end # Test
|
62
|
+
end # Mutant
|
data/lib/mutant/timer.rb
CHANGED
data/lib/mutant/version.rb
CHANGED
data/lib/mutant/world.rb
CHANGED
data/lib/mutant.rb
CHANGED
@@ -74,7 +74,6 @@ module Mutant
|
|
74
74
|
require 'mutant/bootstrap'
|
75
75
|
require 'mutant/version'
|
76
76
|
require 'mutant/env'
|
77
|
-
require 'mutant/pipe'
|
78
77
|
require 'mutant/util'
|
79
78
|
require 'mutant/registry'
|
80
79
|
require 'mutant/ast'
|
@@ -102,6 +101,8 @@ module Mutant
|
|
102
101
|
require 'mutant/isolation/fork'
|
103
102
|
require 'mutant/isolation/none'
|
104
103
|
require 'mutant/parallel'
|
104
|
+
require 'mutant/parallel/connection'
|
105
|
+
require 'mutant/parallel/pipe'
|
105
106
|
require 'mutant/parallel/driver'
|
106
107
|
require 'mutant/parallel/source'
|
107
108
|
require 'mutant/parallel/worker'
|
@@ -204,6 +205,8 @@ module Mutant
|
|
204
205
|
require 'mutant/expression/namespace'
|
205
206
|
require 'mutant/expression/parser'
|
206
207
|
require 'mutant/test'
|
208
|
+
require 'mutant/test/runner'
|
209
|
+
require 'mutant/test/runner/sink'
|
207
210
|
require 'mutant/timer'
|
208
211
|
require 'mutant/integration'
|
209
212
|
require 'mutant/integration/null'
|
@@ -243,6 +246,7 @@ module Mutant
|
|
243
246
|
require 'mutant/reporter/cli/printer/mutation_result'
|
244
247
|
require 'mutant/reporter/cli/printer/status_progressive'
|
245
248
|
require 'mutant/reporter/cli/printer/subject_result'
|
249
|
+
require 'mutant/reporter/cli/printer/test'
|
246
250
|
require 'mutant/reporter/cli/format'
|
247
251
|
require 'mutant/repository'
|
248
252
|
require 'mutant/repository/diff'
|
@@ -327,7 +331,9 @@ module Mutant
|
|
327
331
|
recorder: recorder,
|
328
332
|
stderr: $stderr,
|
329
333
|
stdout: $stdout,
|
334
|
+
tempfile: Tempfile,
|
330
335
|
thread: Thread,
|
336
|
+
time: Time,
|
331
337
|
timer: timer
|
332
338
|
)
|
333
339
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mutant
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.11.
|
4
|
+
version: 0.11.29
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Markus Schirp
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-03-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: diff-lcs
|
@@ -44,14 +44,14 @@ dependencies:
|
|
44
44
|
requirements:
|
45
45
|
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: 2.
|
47
|
+
version: 2.9.0
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: 2.
|
54
|
+
version: 2.9.0
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: sorbet-runtime
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -80,20 +80,6 @@ dependencies:
|
|
80
80
|
- - "~>"
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: 0.6.9
|
83
|
-
- !ruby/object:Gem::Dependency
|
84
|
-
name: parallel
|
85
|
-
requirement: !ruby/object:Gem::Requirement
|
86
|
-
requirements:
|
87
|
-
- - "~>"
|
88
|
-
- !ruby/object:Gem::Version
|
89
|
-
version: '1.3'
|
90
|
-
type: :development
|
91
|
-
prerelease: false
|
92
|
-
version_requirements: !ruby/object:Gem::Requirement
|
93
|
-
requirements:
|
94
|
-
- - "~>"
|
95
|
-
- !ruby/object:Gem::Version
|
96
|
-
version: '1.3'
|
97
83
|
- !ruby/object:Gem::Dependency
|
98
84
|
name: rspec
|
99
85
|
requirement: !ruby/object:Gem::Requirement
|
@@ -305,11 +291,12 @@ files:
|
|
305
291
|
- lib/mutant/mutator/util/array.rb
|
306
292
|
- lib/mutant/mutator/util/symbol.rb
|
307
293
|
- lib/mutant/parallel.rb
|
294
|
+
- lib/mutant/parallel/connection.rb
|
308
295
|
- lib/mutant/parallel/driver.rb
|
296
|
+
- lib/mutant/parallel/pipe.rb
|
309
297
|
- lib/mutant/parallel/source.rb
|
310
298
|
- lib/mutant/parallel/worker.rb
|
311
299
|
- lib/mutant/parser.rb
|
312
|
-
- lib/mutant/pipe.rb
|
313
300
|
- lib/mutant/procto.rb
|
314
301
|
- lib/mutant/range.rb
|
315
302
|
- lib/mutant/registry.rb
|
@@ -327,6 +314,7 @@ files:
|
|
327
314
|
- lib/mutant/reporter/cli/printer/mutation_result.rb
|
328
315
|
- lib/mutant/reporter/cli/printer/status_progressive.rb
|
329
316
|
- lib/mutant/reporter/cli/printer/subject_result.rb
|
317
|
+
- lib/mutant/reporter/cli/printer/test.rb
|
330
318
|
- lib/mutant/reporter/null.rb
|
331
319
|
- lib/mutant/reporter/sequence.rb
|
332
320
|
- lib/mutant/repository.rb
|
@@ -347,6 +335,8 @@ files:
|
|
347
335
|
- lib/mutant/subject/method/metaclass.rb
|
348
336
|
- lib/mutant/subject/method/singleton.rb
|
349
337
|
- lib/mutant/test.rb
|
338
|
+
- lib/mutant/test/runner.rb
|
339
|
+
- lib/mutant/test/runner/sink.rb
|
350
340
|
- lib/mutant/timer.rb
|
351
341
|
- lib/mutant/transform.rb
|
352
342
|
- lib/mutant/util.rb
|
data/lib/mutant/pipe.rb
DELETED
@@ -1,96 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Mutant
|
4
|
-
# Pipe abstraction
|
5
|
-
class Pipe
|
6
|
-
include Adamantium, Anima.new(:reader, :writer)
|
7
|
-
|
8
|
-
# Run block with pipe in binmode
|
9
|
-
#
|
10
|
-
# @return [undefined]
|
11
|
-
def self.with(io)
|
12
|
-
io.pipe(binmode: true) do |(reader, writer)|
|
13
|
-
yield new(reader: reader, writer: writer)
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
def self.from_io(io)
|
18
|
-
reader, writer = io.pipe(binmode: true)
|
19
|
-
new(reader: reader, writer: writer)
|
20
|
-
end
|
21
|
-
|
22
|
-
# Writer end of the pipe
|
23
|
-
#
|
24
|
-
# @return [IO]
|
25
|
-
def to_writer
|
26
|
-
reader.close
|
27
|
-
writer
|
28
|
-
end
|
29
|
-
|
30
|
-
# Parent reader end of the pipe
|
31
|
-
#
|
32
|
-
# @return [IO]
|
33
|
-
def to_reader
|
34
|
-
writer.close
|
35
|
-
reader
|
36
|
-
end
|
37
|
-
|
38
|
-
class Connection
|
39
|
-
include Anima.new(:marshal, :reader, :writer)
|
40
|
-
|
41
|
-
Error = Class.new(RuntimeError)
|
42
|
-
|
43
|
-
class Frame
|
44
|
-
include Anima.new(:io)
|
45
|
-
|
46
|
-
HEADER_FORMAT = 'N'
|
47
|
-
MAX_BYTES = (2**32).pred
|
48
|
-
HEADER_SIZE = 4
|
49
|
-
|
50
|
-
def receive_value
|
51
|
-
header = read(HEADER_SIZE)
|
52
|
-
read(Util.one(header.unpack(HEADER_FORMAT)))
|
53
|
-
end
|
54
|
-
|
55
|
-
def send_value(body)
|
56
|
-
bytesize = body.bytesize
|
57
|
-
|
58
|
-
fail Error, 'message to big' if bytesize > MAX_BYTES
|
59
|
-
|
60
|
-
io.binmode
|
61
|
-
io.write([bytesize].pack(HEADER_FORMAT))
|
62
|
-
io.write(body)
|
63
|
-
end
|
64
|
-
|
65
|
-
private
|
66
|
-
|
67
|
-
def read(bytes)
|
68
|
-
io.binmode
|
69
|
-
io.read(bytes) or fail Error, 'Unexpected EOF'
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
def call(payload)
|
74
|
-
send_value(payload)
|
75
|
-
receive_value
|
76
|
-
end
|
77
|
-
|
78
|
-
def receive_value
|
79
|
-
marshal.load(reader.receive_value)
|
80
|
-
end
|
81
|
-
|
82
|
-
def send_value(value)
|
83
|
-
writer.send_value(marshal.dump(value))
|
84
|
-
self
|
85
|
-
end
|
86
|
-
|
87
|
-
def self.from_pipes(marshal:, reader:, writer:)
|
88
|
-
new(
|
89
|
-
marshal: marshal,
|
90
|
-
reader: Frame.new(io: reader.to_reader),
|
91
|
-
writer: Frame.new(io: writer.to_writer)
|
92
|
-
)
|
93
|
-
end
|
94
|
-
end
|
95
|
-
end # Pipe
|
96
|
-
end # Mutant
|