Skip to content

Commit 4db4417

Browse files
authored
Merge pull request #891 from mbj/add/mutant-world
Add Mutant::World
2 parents ee4bd5d + 6643072 commit 4db4417

File tree

19 files changed

+248
-190
lines changed

19 files changed

+248
-190
lines changed

bin/mutant

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,10 @@ namespace =
3737
Mutant
3838
end
3939

40-
Kernel.exit(namespace::CLI.run(namespace::Config::DEFAULT, ARGV))
40+
Kernel.exit(
41+
namespace::CLI.run(
42+
namespace::WORLD,
43+
namespace::Config::DEFAULT,
44+
ARGV
45+
)
46+
)

lib/mutant.rb

Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -191,39 +191,39 @@ def self.ci?
191191
require 'mutant/zombifier'
192192

193193
module Mutant
194+
WORLD = World.new(
195+
condition_variable: ConditionVariable,
196+
io: IO,
197+
kernel: Kernel,
198+
load_path: $LOAD_PATH,
199+
marshal: Marshal,
200+
mutex: Mutex,
201+
open3: Open3,
202+
pathname: Pathname,
203+
process: Process,
204+
stderr: STDOUT,
205+
stdout: STDOUT,
206+
thread: Thread
207+
)
208+
194209
# Reopen class to initialize constant to avoid dep circle
195210
class Config
196211
DEFAULT = new(
197-
condition_variable: ConditionVariable,
198-
expression_parser: Expression::Parser.new([
212+
expression_parser: Expression::Parser.new([
199213
Expression::Method,
200214
Expression::Methods,
201215
Expression::Namespace::Exact,
202216
Expression::Namespace::Recursive
203217
]),
204-
fail_fast: false,
205-
includes: EMPTY_ARRAY,
206-
integration: Integration::Null,
207-
isolation: Mutant::Isolation::Fork.new(
208-
io: IO,
209-
marshal: Marshal,
210-
process: Process,
211-
stderr: STDOUT,
212-
stdout: STDOUT
213-
),
214-
jobs: Etc.nprocessors,
215-
kernel: Kernel,
216-
load_path: $LOAD_PATH,
217-
matcher: Matcher::Config::DEFAULT,
218-
mutex: Mutex,
219-
open3: Open3,
220-
pathname: Pathname,
221-
reporter: Reporter::CLI.build(STDOUT),
222-
requires: EMPTY_ARRAY,
223-
stderr: STDERR,
224-
stdout: STDOUT,
225-
thread: Thread,
226-
zombie: false
218+
fail_fast: false,
219+
includes: EMPTY_ARRAY,
220+
integration: Integration::Null,
221+
isolation: Mutant::Isolation::Fork.new(WORLD),
222+
jobs: Etc.nprocessors,
223+
matcher: Matcher::Config::DEFAULT,
224+
reporter: Reporter::CLI.build(WORLD.stdout),
225+
requires: EMPTY_ARRAY,
226+
zombie: false
227227
)
228228
end # Config
229229
end # Mutant

lib/mutant/cli.rb

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ module Mutant
55
#
66
# rubocop:disable Metrics/ClassLength
77
class CLI
8-
include Concord.new(:config)
8+
include Concord.new(:world, :config)
99

1010
private_class_method :new
1111

@@ -21,6 +21,9 @@ class CLI
2121

2222
# Run cli with arguments
2323
#
24+
# @param [World] world
25+
# the outside world
26+
#
2427
# @param [Config] config
2528
# the default config
2629
#
@@ -30,24 +33,29 @@ class CLI
3033
# @return [Boolean]
3134
#
3235
# rubocop:disable Style/Semicolon
33-
def self.run(config, arguments)
34-
apply(config, arguments)
35-
.fmap(&Env::Bootstrap.method(:call))
36+
#
37+
# ignore :reek:LongParameterList
38+
def self.run(world, config, arguments)
39+
apply(world, config, arguments)
40+
.fmap { |cli_config| Env::Bootstrap.call(world, cli_config) }
3641
.fmap(&Runner.method(:call))
37-
.from_right { |error| config.stderr.puts(error); return false }
42+
.from_right { |error| world.stderr.puts(error); return false }
3843
.success?
3944
end
4045
# rubocop:enable Style/Semicolon
4146

4247
# Parse arguments into config
4348
#
49+
# @param [World] world
4450
# @param [Config] config
4551
# @param [Array<String>] arguments
4652
#
4753
# @return [Either<OptionParser::ParseError, Config>]
48-
def self.apply(config, arguments)
54+
#
55+
# ignore :reek:LongParameterList
56+
def self.apply(world, config, arguments)
4957
Either
50-
.wrap_error(OptionParser::ParseError) { new(config).parse(arguments) }
58+
.wrap_error(OptionParser::ParseError) { new(world, config).parse(arguments) }
5159
.lmap(&:message)
5260
end
5361

@@ -119,7 +127,7 @@ def add_environment_options(opts)
119127
#
120128
# @return [undefined]
121129
def setup_integration(name)
122-
with(integration: Integration.setup(config.kernel, name))
130+
with(integration: Integration.setup(world.kernel, name))
123131
rescue LoadError
124132
raise(
125133
OptionParser::InvalidArgument,
@@ -153,9 +161,9 @@ def add_filter_options(opts)
153161
:subject_filters,
154162
Repository::SubjectFilter.new(
155163
Repository::Diff.new(
156-
config: config,
157-
from: Repository::Diff::HEAD,
158-
to: revision
164+
from: Repository::Diff::HEAD,
165+
to: revision,
166+
world: world
159167
)
160168
)
161169
)
@@ -172,12 +180,12 @@ def add_debug_options(opts)
172180
with(fail_fast: true)
173181
end
174182
opts.on('--version', 'Print mutants version') do
175-
config.stdout.puts("mutant-#{VERSION}")
176-
config.kernel.exit
183+
world.stdout.puts("mutant-#{VERSION}")
184+
world.kernel.exit
177185
end
178186
opts.on_tail('-h', '--help', 'Show this message') do
179-
config.stdout.puts(opts.to_s)
180-
config.kernel.exit
187+
world.stdout.puts(opts.to_s)
188+
world.kernel.exit
181189
end
182190
end
183191

lib/mutant/config.rb

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,39 @@
11
# frozen_string_literal: true
22

33
module Mutant
4+
# The outer world IO objects mutant does interact with
5+
class World
6+
include Adamantium::Flat, Anima.new(
7+
:condition_variable,
8+
:io,
9+
:kernel,
10+
:load_path,
11+
:marshal,
12+
:mutex,
13+
:open3,
14+
:pathname,
15+
:process,
16+
:stderr,
17+
:stdout,
18+
:thread
19+
)
20+
end # World
21+
422
# Standalone configuration of a mutant execution.
523
#
624
# Does not reference any "external" volatile state. The configuration applied
725
# to current environment is being represented by the Mutant::Env object.
826
class Config
927
include Adamantium::Flat, Anima.new(
10-
:condition_variable,
1128
:expression_parser,
1229
:fail_fast,
1330
:includes,
1431
:integration,
1532
:isolation,
1633
:jobs,
17-
:kernel,
18-
:load_path,
1934
:matcher,
20-
:mutex,
21-
:open3,
22-
:pathname,
2335
:reporter,
2436
:requires,
25-
:stderr,
26-
:stdout,
27-
:thread,
2837
:zombie
2938
)
3039

lib/mutant/env.rb

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ class Env
1010
:mutations,
1111
:parser,
1212
:selector,
13-
:subjects
13+
:subjects,
14+
:world
1415
)
1516

1617
SEMANTICS_MESSAGE =
@@ -49,7 +50,7 @@ def selections
4950
# @return [Result::Isolation]
5051
def run_mutation_tests(mutation)
5152
config.isolation.call do
52-
result = mutation.insert(config.kernel)
53+
result = mutation.insert(world.kernel)
5354

5455
if result.equal?(Loader::Result::VoidValue.instance)
5556
Result::Test::VoidValue.instance

lib/mutant/env/bootstrap.rb

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ module Mutant
44
class Env
55
# Bootstrap environment
66
class Bootstrap
7-
include Adamantium::Flat, Concord::Public.new(:config)
7+
include Adamantium::Flat, Concord::Public.new(:world, :config)
88

99
SEMANTICS_MESSAGE_FORMAT =
1010
"%<message>s. Fix your lib to follow normal ruby semantics!\n" \
@@ -30,11 +30,12 @@ class Bootstrap
3030

3131
# Run Bootstrap
3232
#
33+
# @param [World] world
3334
# @param [Config] config
3435
#
3536
# @return [Env]
36-
def self.call(config)
37-
new(config).env
37+
def self.call(world, config)
38+
new(world, config).env
3839
end
3940

4041
# Initialize object
@@ -72,7 +73,8 @@ def env
7273
mutations: subjects.flat_map(&:mutations),
7374
parser: parser,
7475
selector: Selector::Expression.new(integration),
75-
subjects: subjects
76+
subjects: subjects,
77+
world: world
7678
)
7779
end
7880

@@ -108,8 +110,8 @@ def scope_name(scope)
108110
#
109111
# @return [undefined]
110112
def infect
111-
config.includes.each(&config.load_path.method(:<<))
112-
config.requires.each(&config.kernel.method(:require))
113+
config.includes.each(&world.load_path.method(:<<))
114+
config.requires.each(&world.kernel.method(:require))
113115
end
114116

115117
# Matched subjects

lib/mutant/isolation/fork.rb

Lines changed: 15 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,11 @@ module Mutant
44
class Isolation
55
# Isolation via the fork(2) systemcall.
66
class Fork < self
7-
include(
8-
Adamantium::Flat,
9-
Anima.new(:io, :marshal, :process, :stderr, :stdout)
10-
)
7+
include(Adamantium::Flat, Concord.new(:world))
118

129
READ_SIZE = 4096
1310

14-
ATTRIBUTES =
15-
(anima.attribute_names + %i[block log_pipe result_pipe]).freeze
11+
ATTRIBUTES = %i[block log_pipe result_pipe world].freeze
1612

1713
# Unsucessful result as child exited nonzero
1814
class ChildError < Result
@@ -57,7 +53,7 @@ def parent
5753
# ignore :reek:InstanceVariableAssumption
5854
class Parent
5955
include(
60-
Anima.new(*(ATTRIBUTES + %i[io])),
56+
Anima.new(*ATTRIBUTES),
6157
Procto.call
6258
)
6359

@@ -81,7 +77,7 @@ def call
8177
#
8278
# @return [Integer]
8379
def start_child
84-
process.fork do
80+
world.process.fork do
8581
Child.call(
8682
to_h.merge(
8783
log_pipe: log_pipe.child,
@@ -108,7 +104,7 @@ def read_child_result(pid)
108104
)
109105

110106
begin
111-
result = marshal.load(result_fragments.join)
107+
result = world.marshal.load(result_fragments.join)
112108
rescue ArgumentError => exception
113109
add_result(Result::Exception.new(exception))
114110
else
@@ -126,7 +122,7 @@ def read_child_result(pid)
126122
# @return [undefined]
127123
def read_fragments(targets)
128124
until targets.empty?
129-
ready, = io.select(targets.keys)
125+
ready, = world.io.select(targets.keys)
130126

131127
ready.each do |fd|
132128
if fd.eof?
@@ -144,7 +140,7 @@ def read_fragments(targets)
144140
#
145141
# @return [undefined]
146142
def wait_child(pid, log_fragments)
147-
_pid, status = process.wait2(pid)
143+
_pid, status = world.process.wait2(pid)
148144

149145
unless status.success? # rubocop:disable Style/GuardClause
150146
add_result(ChildError.new(status, log_fragments.join))
@@ -170,9 +166,9 @@ class Child
170166
#
171167
# @return [undefined]
172168
def call
173-
stderr.reopen(log_pipe)
174-
stdout.reopen(log_pipe)
175-
result_pipe.syswrite(marshal.dump(block.call))
169+
world.stderr.reopen(log_pipe)
170+
world.stdout.reopen(log_pipe)
171+
result_pipe.syswrite(world.marshal.dump(block.call))
176172
result_pipe.close
177173
end
178174

@@ -189,14 +185,14 @@ def call
189185
#
190186
# rubocop:disable Metrics/MethodLength
191187
def call(&block)
188+
io = world.io
192189
Pipe.with(io) do |result|
193190
Pipe.with(io) do |log|
194191
Parent.call(
195-
to_h.merge(
196-
block: block,
197-
log_pipe: log,
198-
result_pipe: result
199-
)
192+
block: block,
193+
log_pipe: log,
194+
result_pipe: result,
195+
world: world
200196
)
201197
end
202198
end

lib/mutant/matcher/method.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ def ast
8787
#
8888
# @return [Pathname]
8989
def source_path
90-
env.config.pathname.new(source_location.first)
90+
env.world.pathname.new(source_location.first)
9191
end
9292
memoize :source_path
9393

0 commit comments

Comments
 (0)