Skip to content

Commit

Permalink
Fix config object attribute primitivity
Browse files Browse the repository at this point in the history
* Reduces the amount of complex objects being referenced from
  Mutant::Config
* Prepares to read the config from other sources than the CLI, (AKA
  config file)
* Opens the door for configuring more details, Mutant::Config is
  supposed to be the source of "primitive truth", where Mutant::Env is
  the compiled version that applied the config to the env.
* Fix adjacent mutations in Mutant::Env namespace
  • Loading branch information
mbj committed Sep 20, 2015
1 parent 25f87d0 commit c7ce3cb
Show file tree
Hide file tree
Showing 22 changed files with 209 additions and 142 deletions.
2 changes: 1 addition & 1 deletion config/flay.yml
@@ -1,3 +1,3 @@
---
threshold: 18
total_score: 1234
total_score: 1239
22 changes: 13 additions & 9 deletions lib/mutant.rb
Expand Up @@ -193,22 +193,26 @@ class Config

DEFAULT = new(
debug: false,
expected_coverage: Rational(1),
fail_fast: false,
integration: Integration::Null,
matcher: Matcher::Config::DEFAULT,
includes: EMPTY_ARRAY,
integration: 'null'.freeze,
jobs: Mutant.ci? ? CI_DEFAULT_PROCESSOR_COUNT : ::Parallel.processor_count,
matcher: Matcher::Config::DEFAULT,
requires: EMPTY_ARRAY,
isolation: Mutant::Isolation::Fork,
reporter: Reporter::CLI.build($stdout),
zombie: false,
jobs: Mutant.ci? ? CI_DEFAULT_PROCESSOR_COUNT : ::Parallel.processor_count,
expected_coverage: Rational(1),
expression_parser: Expression::Parser.new([
zombie: false
)
end # Config

class Expression
class Parser
DEFAULT = Expression::Parser.new([
Expression::Method,
Expression::Methods,
Expression::Namespace::Exact,
Expression::Namespace::Recursive
])
)
end # Config
end # Parser
end # Expression
end # Mutant
9 changes: 5 additions & 4 deletions lib/mutant/cli.rb
Expand Up @@ -82,7 +82,7 @@ def parse_match_expressions(expressions)
fail Error, 'No expressions given' if expressions.empty?

expressions.each do |expression|
add_matcher(:match_expressions, config.expression_parser.(expression))
add_matcher(:match_expressions, expression)
end
end

Expand Down Expand Up @@ -119,9 +119,10 @@ def add_environment_options(opts)
#
# @api private
def setup_integration(name)
with(integration: Integration.setup(name))
Integration.setup(name)
with(integration: name)
rescue LoadError
raise Error, "Could not load integration #{name.inspect} (you may want to try installing the gem mutant-#{name})"
raise Error, "Could not load integration #{name.inspect} (install the gem mutant-#{name} if exists)"
end

# Add mutation options
Expand Down Expand Up @@ -153,7 +154,7 @@ def add_mutation_options(opts)
# @api private
def add_filter_options(opts)
opts.on('--ignore-subject EXPRESSION', 'Ignore subjects that match EXPRESSION as prefix') do |pattern|
add_matcher(:ignore_expressions, config.expression_parser.(pattern))
add_matcher(:ignore_expressions, pattern)
end
opts.on('--since REVISION', 'Only select subjects touched since REVISION') do |revision|
add_matcher(:subject_filters, Repository::SubjectFilter.new(Repository::Diff.from_head(revision)))
Expand Down
12 changes: 5 additions & 7 deletions lib/mutant/config.rb
Expand Up @@ -6,17 +6,15 @@ module Mutant
class Config
include Adamantium::Flat, Anima.new(
:debug,
:expected_coverage,
:fail_fast,
:includes,
:integration,
:jobs,
:matcher,
:includes,
:requires,
:reporter,
:isolation,
:fail_fast,
:jobs,
:zombie,
:expected_coverage,
:expression_parser
:zombie
)

%i[fail_fast zombie debug].each do |name|
Expand Down
10 changes: 6 additions & 4 deletions lib/mutant/env.rb
Expand Up @@ -2,13 +2,15 @@ module Mutant
# Abstract base class for mutant environments
class Env
include Adamantium::Flat, Anima.new(
:config,
:actor_env,
:cache,
:subjects,
:matchable_scopes,
:config,
:integration,
:expression_parser,
:isolation,
:selector,
:subjects,
:matchable_scopes,
:mutations
)

Expand Down Expand Up @@ -59,7 +61,7 @@ def run_mutation_tests(mutation)
start = Time.now
tests = selector.call(mutation.subject)

config.isolation.call do
isolation.call do
mutation.insert
integration.call(tests)
end
Expand Down
36 changes: 25 additions & 11 deletions lib/mutant/env/bootstrap.rb
Expand Up @@ -15,6 +15,13 @@ class Bootstrap
# @api private
attr_reader :matchable_scopes

# The expression parser
#
# @return [Expression::Parser]
#
# @api private
attr_reader :expression_parser

# New bootstrap env
#
# @return [Env]
Expand All @@ -31,6 +38,7 @@ def self.new(_config, _cache = Cache.new)
# @api private
def initialize(*)
super
@expression_parser = Expression::Parser::DEFAULT
infect
initialize_matchable_scopes
end
Expand All @@ -57,14 +65,16 @@ def warn(message)
def env
subjects = matched_subjects
Env.new(
actor_env: Actor::Env.new(Thread),
config: config,
cache: cache,
subjects: subjects,
matchable_scopes: matchable_scopes,
integration: @integration,
selector: Selector::Expression.new(@integration),
mutations: subjects.flat_map(&:mutations)
actor_env: Actor::Env.new(Thread),
cache: cache,
config: config,
expression_parser: expression_parser,
integration: @integration,
isolation: Isolation::Fork,
matchable_scopes: matchable_scopes,
mutations: subjects.flat_map(&:mutations),
selector: Selector::Expression.new(@integration),
subjects: subjects
)
end

Expand Down Expand Up @@ -96,7 +106,11 @@ def scope_name(scope)
def infect
config.includes.each(&$LOAD_PATH.method(:<<))
config.requires.each(&Kernel.method(:require))
@integration = config.integration.new(config.expression_parser).setup

@integration = Integration
.lookup(config.integration)
.new(expression_parser)
.setup
end

# Matched subjects
Expand All @@ -105,7 +119,7 @@ def infect
#
# @api private
def matched_subjects
Matcher::Compiler.call(config.matcher).call(self)
Matcher::Compiler.call(config.matcher, expression_parser).call(self)
end

# Initialize matchable scopes
Expand Down Expand Up @@ -141,7 +155,7 @@ def expression(scope)
return
end

config.expression_parser.try_parse(name)
expression_parser.try_parse(name)
end
end # Boostrap
end # Env
Expand Down
32 changes: 25 additions & 7 deletions lib/mutant/matcher/compiler.rb
Expand Up @@ -3,21 +3,25 @@ class Matcher

# Compiler for complex matchers
class Compiler
include Concord.new(:config), AST::Sexp, Procto.call(:result)
include Concord.new(:config, :expression_parser), AST::Sexp, Procto.call(:result)

# Generated matcher
#
# @return [Matcher]
#
# @api private
#
# rubocop:disable MethodLength
def result
Filter.new(
Chain.new(config.match_expressions.map(&:matcher)),
Chain.new(
config
.match_expressions
.map(&method(:parse_expression))
.map(&:matcher)
),
Morpher::Evaluator::Predicate::Boolean::And.new(
[
ignored_subjects,
filtered_subjects
]
[ignored_subjects, filtered_subjects]
)
)
end
Expand Down Expand Up @@ -47,7 +51,10 @@ def call(subject)
def ignored_subjects
Morpher::Evaluator::Predicate::Boolean::Negation.new(
Morpher::Evaluator::Predicate::Boolean::Or.new(
config.ignore_expressions.map(&SubjectPrefix.method(:new))
config
.ignore_expressions
.map(&method(:parse_expression))
.map(&SubjectPrefix.method(:new))
)
)
end
Expand All @@ -61,6 +68,17 @@ def filtered_subjects
Morpher::Evaluator::Predicate::Boolean::And.new(config.subject_filters)
end

# Parse expression
#
# @param [String] syntax
#
# @return [Expression]
#
# @api private
def parse_expression(syntax)
expression_parser.call(syntax)
end

end # Compiler
end # Matcher
end # Mutant
4 changes: 2 additions & 2 deletions lib/mutant/matcher/config.rb
Expand Up @@ -14,8 +14,8 @@ class Config
ENUM_DELIMITER = ','.freeze
EMPTY_ATTRIBUTES = 'empty'.freeze
PRESENTATIONS = IceNine.deep_freeze(
match_expressions: :syntax,
ignore_expressions: :syntax,
match_expressions: :to_s,
ignore_expressions: :to_s,
subject_filters: :inspect
)
private_constant(*constants(false))
Expand Down
2 changes: 1 addition & 1 deletion spec/spec_helper.rb
Expand Up @@ -48,7 +48,7 @@ def parse(string)
end

def parse_expression(string)
Mutant::Config::DEFAULT.expression_parser.(string)
Mutant::Expression::Parser::DEFAULT.(string)
end
end

Expand Down
21 changes: 9 additions & 12 deletions spec/unit/mutant/cli_spec.rb
Expand Up @@ -67,13 +67,13 @@

# Defaults
let(:expected_filter) { Morpher.evaluator(s(:true)) }
let(:expected_integration) { Mutant::Integration::Null }
let(:expected_integration) { 'null' }
let(:expected_reporter) { Mutant::Config::DEFAULT.reporter }
let(:expected_matcher_config) { default_matcher_config }

let(:default_matcher_config) do
Mutant::Matcher::Config::DEFAULT
.with(match_expressions: expressions.map(&method(:parse_expression)))
.with(match_expressions: expressions)
end

let(:flags) { [] }
Expand Down Expand Up @@ -153,18 +153,15 @@

it_should_behave_like 'a cli parser'

let(:expected_integration) { Mutant::Integration::Rspec }
let(:expected_integration) { 'rspec' }
end

context 'when integration does NOT exist' do
let(:flags) { %w[--use other] }
context 'when does not' do
let(:flags) { %w[--use foo] }

it 'raises error' do
expect { subject }.to raise_error(
Mutant::CLI::Error,
'Could not load integration "other" (you may want to try installing the gem mutant-other)'
)
end
let(:expected_message) { 'Could not load integration "foo" (install the gem mutant-foo if exists)' }

it_should_behave_like 'an invalid cli run'
end
end

Expand Down Expand Up @@ -249,7 +246,7 @@
let(:flags) { %w[--ignore-subject Foo::Bar] }

let(:expected_matcher_config) do
default_matcher_config.with(ignore_expressions: [parse_expression('Foo::Bar')])
default_matcher_config.with(ignore_expressions: %w[Foo::Bar])
end

it_should_behave_like 'a cli parser'
Expand Down

0 comments on commit c7ce3cb

Please sign in to comment.