mutant 0.11.28 → 0.11.29
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.
- 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
|