Use environment object inside matchers
This will allow to use reporters from matchers avoiding stupid direct writes to $stderr. Also it will allow to remove matching from CLI altogether in a phase of the runner. Allowing to decouple Mutant::Config from VM environment allowing to serialize it ;) Step by step. Takes a while.
This commit is contained in:
parent
a18a15f8ec
commit
3390abc675
19 changed files with 96 additions and 60 deletions
|
@ -1,3 +1,3 @@
|
|||
---
|
||||
threshold: 18
|
||||
total_score: 968
|
||||
total_score: 976
|
||||
|
|
|
@ -79,6 +79,7 @@ module Mutant
|
|||
end # Mutant
|
||||
|
||||
require 'mutant/version'
|
||||
require 'mutant/env'
|
||||
require 'mutant/ast'
|
||||
require 'mutant/ast/sexp'
|
||||
require 'mutant/ast/types'
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
module Mutant
|
||||
# An AST cache
|
||||
class Cache
|
||||
include Equalizer.new
|
||||
include Equalizer.new, Adamantium::Mutable
|
||||
|
||||
# Initialize object
|
||||
#
|
||||
|
|
|
@ -39,7 +39,7 @@ module Mutant
|
|||
# @api private
|
||||
#
|
||||
def initialize(arguments = [])
|
||||
@builder = Matcher::Builder.new(Cache.new)
|
||||
@builder = Matcher::Builder.new(Env::Boot.new(Reporter::CLI.new($stderr), Cache.new))
|
||||
@debug = @fail_fast = @zombie = false
|
||||
@expected_coverage = 100.0
|
||||
@integration = Integration::Null.new
|
||||
|
|
36
lib/mutant/env.rb
Normal file
36
lib/mutant/env.rb
Normal file
|
@ -0,0 +1,36 @@
|
|||
module Mutant
|
||||
# Abstract base class for mutant environments
|
||||
class Env
|
||||
include AbstractType, Adamantium
|
||||
|
||||
# Return config
|
||||
#
|
||||
# @return [Config]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
abstract_method :config
|
||||
|
||||
# Return cache
|
||||
#
|
||||
# @return [Cache]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
abstract_method :cache
|
||||
|
||||
# Return reporter
|
||||
#
|
||||
# @return [Reporter]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
abstract_method :reporter
|
||||
|
||||
# Boot environment used for matching
|
||||
class Boot < self
|
||||
include Concord::Public.new(:reporter, :cache)
|
||||
end # Boot
|
||||
|
||||
end # ENV
|
||||
end # Mutant
|
|
@ -5,7 +5,7 @@ module Mutant
|
|||
|
||||
# Default matcher build implementation
|
||||
#
|
||||
# @param [Cache] cache
|
||||
# @param [Env] env
|
||||
# @param [Object] input
|
||||
#
|
||||
# @return [undefined]
|
||||
|
|
|
@ -2,17 +2,17 @@ module Mutant
|
|||
class Matcher
|
||||
# Builder for complex matchers
|
||||
class Builder
|
||||
include Concord.new(:cache), AST::Sexp
|
||||
include Concord.new(:env), AST::Sexp
|
||||
|
||||
# Initalize object
|
||||
#
|
||||
# @param [Cache] cache
|
||||
# @param [Cache] env
|
||||
#
|
||||
# @return [undefined]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def initialize(cache)
|
||||
def initialize(env)
|
||||
super
|
||||
@matchers = []
|
||||
@subject_ignores = []
|
||||
|
@ -28,7 +28,7 @@ module Mutant
|
|||
# @api private
|
||||
#
|
||||
def add_subject_ignore(expression)
|
||||
@subject_ignores << expression.matcher(cache)
|
||||
@subject_ignores << expression.matcher(env)
|
||||
self
|
||||
end
|
||||
|
||||
|
@ -54,7 +54,7 @@ module Mutant
|
|||
# @api private
|
||||
#
|
||||
def add_match_expression(expression)
|
||||
@matchers << expression.matcher(cache)
|
||||
@matchers << expression.matcher(env)
|
||||
self
|
||||
end
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ module Mutant
|
|||
class Matcher
|
||||
# Matcher for subjects that are a specific method
|
||||
class Method < self
|
||||
include Adamantium::Flat, Concord::Public.new(:cache, :scope, :method)
|
||||
include Adamantium::Flat, Concord::Public.new(:env, :scope, :method)
|
||||
include Equalizer.new(:identification)
|
||||
|
||||
# Methods within rbx kernel directory are precompiled and their source
|
||||
|
@ -78,7 +78,7 @@ module Mutant
|
|||
# @api private
|
||||
#
|
||||
def ast
|
||||
cache.parse(source_path)
|
||||
env.cache.parse(source_path)
|
||||
end
|
||||
|
||||
# Return path to source
|
||||
|
|
|
@ -7,7 +7,7 @@ module Mutant
|
|||
|
||||
# Dispatching builder, detects memoizable case
|
||||
#
|
||||
# @param [Cache] cache
|
||||
# @param [Env] env
|
||||
# @param [Class, Module] scope
|
||||
# @param [UnboundMethod] method
|
||||
#
|
||||
|
@ -15,10 +15,10 @@ module Mutant
|
|||
#
|
||||
# @api private
|
||||
#
|
||||
def self.build(cache, scope, method)
|
||||
def self.build(env, scope, method)
|
||||
name = method.name
|
||||
if scope.ancestors.include?(::Memoizable) && scope.memoized?(name)
|
||||
return Memoized.new(cache, scope, method)
|
||||
return Memoized.new(env, scope, method)
|
||||
end
|
||||
super
|
||||
end
|
||||
|
|
|
@ -2,7 +2,7 @@ module Mutant
|
|||
class Matcher
|
||||
# Abstract base class for matcher that returns method subjects from scope
|
||||
class Methods < self
|
||||
include AbstractType, Concord::Public.new(:cache, :scope)
|
||||
include AbstractType, Concord::Public.new(:env, :scope)
|
||||
|
||||
# Enumerate subjects
|
||||
#
|
||||
|
@ -56,7 +56,7 @@ module Mutant
|
|||
#
|
||||
def subjects
|
||||
methods.map do |method|
|
||||
matcher.build(cache, scope, method)
|
||||
matcher.build(env, scope, method)
|
||||
end.flat_map(&:to_a)
|
||||
end
|
||||
memoize :subjects
|
||||
|
|
|
@ -5,7 +5,7 @@ module Mutant
|
|||
#
|
||||
# rubocop:disable LineLength
|
||||
class Namespace < self
|
||||
include Concord::Public.new(:cache, :expression)
|
||||
include Concord::Public.new(:env, :expression)
|
||||
|
||||
# Enumerate subjects
|
||||
#
|
||||
|
@ -36,7 +36,7 @@ module Mutant
|
|||
#
|
||||
def scopes
|
||||
::ObjectSpace.each_object(Module).each_with_object([]) do |scope, aggregate|
|
||||
aggregate << Scope.new(cache, scope) if match?(scope)
|
||||
aggregate << Scope.new(env, scope) if match?(scope)
|
||||
end.sort_by(&:identification)
|
||||
end
|
||||
memoize :scopes
|
||||
|
|
|
@ -2,7 +2,7 @@ module Mutant
|
|||
class Matcher
|
||||
# Matcher for specific namespace
|
||||
class Scope < self
|
||||
include Concord::Public.new(:cache, :scope)
|
||||
include Concord::Public.new(:env, :scope)
|
||||
|
||||
MATCHERS = [
|
||||
Matcher::Methods::Singleton,
|
||||
|
@ -34,7 +34,7 @@ module Mutant
|
|||
return to_enum unless block_given?
|
||||
|
||||
MATCHERS.each do |matcher|
|
||||
matcher.new(cache, scope).each(&block)
|
||||
matcher.new(env, scope).each(&block)
|
||||
end
|
||||
|
||||
self
|
||||
|
|
|
@ -30,8 +30,8 @@ $LOAD_PATH << File.join(TestApp.root, 'lib')
|
|||
require 'test_app'
|
||||
|
||||
module Fixtures
|
||||
AST_CACHE = Mutant::Cache.new
|
||||
end
|
||||
BOOT_ENV = Mutant::Env::Boot.new(Mutant::Reporter::CLI.new(STDERR), Mutant::Cache.new)
|
||||
end # Fixtures
|
||||
|
||||
module ParserHelper
|
||||
def generate(node)
|
||||
|
|
|
@ -30,7 +30,7 @@ describe Mutant::CLI, '.new' do
|
|||
let(:expected_reporter) { Mutant::Reporter::CLI.new($stdout) }
|
||||
|
||||
let(:ns) { Mutant::Matcher }
|
||||
let(:cache) { Mutant::Cache.new }
|
||||
let(:env) { Fixtures::BOOT_ENV }
|
||||
|
||||
let(:cli) { object.new(arguments) }
|
||||
|
||||
|
@ -71,7 +71,7 @@ describe Mutant::CLI, '.new' do
|
|||
let(:arguments) { %w[TestApp::Literal#float] }
|
||||
|
||||
let(:expected_matcher) do
|
||||
ns::Method::Instance.new(cache, TestApp::Literal, TestApp::Literal.instance_method(:float))
|
||||
ns::Method::Instance.new(env, TestApp::Literal, TestApp::Literal.instance_method(:float))
|
||||
end
|
||||
|
||||
it_should_behave_like 'a cli parser'
|
||||
|
@ -80,7 +80,7 @@ describe Mutant::CLI, '.new' do
|
|||
context 'with debug flag' do
|
||||
let(:pattern) { 'TestApp*' }
|
||||
let(:arguments) { %W[--debug #{pattern}] }
|
||||
let(:expected_matcher) { ns::Namespace.new(cache, Mutant::Expression.parse(pattern)) }
|
||||
let(:expected_matcher) { ns::Namespace.new(env, Mutant::Expression.parse(pattern)) }
|
||||
|
||||
it_should_behave_like 'a cli parser'
|
||||
|
||||
|
@ -92,7 +92,7 @@ describe Mutant::CLI, '.new' do
|
|||
context 'with zombie flag' do
|
||||
let(:pattern) { 'TestApp*' }
|
||||
let(:arguments) { %W[--zombie #{pattern}] }
|
||||
let(:expected_matcher) { ns::Namespace.new(cache, Mutant::Expression.parse(pattern)) }
|
||||
let(:expected_matcher) { ns::Namespace.new(env, Mutant::Expression.parse(pattern)) }
|
||||
|
||||
it_should_behave_like 'a cli parser'
|
||||
|
||||
|
@ -104,7 +104,7 @@ describe Mutant::CLI, '.new' do
|
|||
context 'with namespace pattern' do
|
||||
let(:pattern) { 'TestApp*' }
|
||||
let(:arguments) { [pattern] }
|
||||
let(:expected_matcher) { ns::Namespace.new(cache, Mutant::Expression.parse(pattern)) }
|
||||
let(:expected_matcher) { ns::Namespace.new(env, Mutant::Expression.parse(pattern)) }
|
||||
|
||||
it_should_behave_like 'a cli parser'
|
||||
end
|
||||
|
@ -124,7 +124,7 @@ describe Mutant::CLI, '.new' do
|
|||
|
||||
let(:expected_matcher) do
|
||||
matcher = ns::Method::Instance.new(
|
||||
cache,
|
||||
env,
|
||||
TestApp::Literal, TestApp::Literal.instance_method(:float)
|
||||
)
|
||||
predicate = Morpher.compile(
|
||||
|
|
|
@ -3,12 +3,12 @@ require 'spec_helper'
|
|||
# rubocop:disable ClassAndModuleChildren
|
||||
describe Mutant::Matcher::Method::Instance do
|
||||
|
||||
let(:cache) { Fixtures::AST_CACHE }
|
||||
let(:env) { Fixtures::BOOT_ENV }
|
||||
|
||||
describe '#each' do
|
||||
subject { object.each { |subject| yields << subject } }
|
||||
|
||||
let(:object) { described_class.new(cache, scope, method) }
|
||||
let(:object) { described_class.new(env, scope, method) }
|
||||
let(:method) { scope.instance_method(method_name) }
|
||||
let(:yields) { [] }
|
||||
let(:namespace) { self.class }
|
||||
|
@ -118,7 +118,7 @@ describe Mutant::Matcher::Method::Instance do
|
|||
describe '.build' do
|
||||
let(:object) { described_class }
|
||||
|
||||
subject { object.build(cache, scope, method) }
|
||||
subject { object.build(env, scope, method) }
|
||||
|
||||
let(:scope) do
|
||||
Class.new do
|
||||
|
@ -141,13 +141,13 @@ describe Mutant::Matcher::Method::Instance do
|
|||
context 'with unmemoized method' do
|
||||
let(:method_name) { :bar }
|
||||
|
||||
it { should eql(described_class.new(cache, scope, method)) }
|
||||
it { should eql(described_class.new(env, scope, method)) }
|
||||
end
|
||||
|
||||
context 'with memoized method' do
|
||||
let(:method_name) { :foo }
|
||||
|
||||
it { should eql(described_class::Memoized.new(cache, scope, method)) }
|
||||
it { should eql(described_class::Memoized.new(env, scope, method)) }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,9 +4,9 @@ require 'spec_helper'
|
|||
describe Mutant::Matcher::Method::Singleton, '#each' do
|
||||
subject { object.each { |subject| yields << subject } }
|
||||
|
||||
let(:object) { described_class.new(cache, scope, method) }
|
||||
let(:object) { described_class.new(env, scope, method) }
|
||||
let(:method) { scope.method(method_name) }
|
||||
let(:cache) { Fixtures::AST_CACHE }
|
||||
let(:env) { Fixtures::BOOT_ENV }
|
||||
let(:yields) { [] }
|
||||
let(:namespace) { self.class }
|
||||
let(:scope) { self.class::Foo }
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Mutant::Matcher::Methods::Instance, '#each' do
|
||||
let(:object) { described_class.new(cache, Foo) }
|
||||
let(:cache) { Mutant::Cache.new }
|
||||
let(:object) { described_class.new(env, Foo) }
|
||||
let(:env) { Fixtures::BOOT_ENV }
|
||||
|
||||
subject { object.each { |matcher| yields << matcher } }
|
||||
|
||||
|
@ -46,9 +46,9 @@ describe Mutant::Matcher::Methods::Instance, '#each' do
|
|||
|
||||
before do
|
||||
matcher = Mutant::Matcher::Method::Instance
|
||||
allow(matcher).to receive(:new).with(cache, Foo, Foo.instance_method(:method_a)).and_return([subject_a])
|
||||
allow(matcher).to receive(:new).with(cache, Foo, Foo.instance_method(:method_b)).and_return([subject_b])
|
||||
allow(matcher).to receive(:new).with(cache, Foo, Foo.instance_method(:method_c)).and_return([subject_c])
|
||||
allow(matcher).to receive(:new).with(env, Foo, Foo.instance_method(:method_a)).and_return([subject_a])
|
||||
allow(matcher).to receive(:new).with(env, Foo, Foo.instance_method(:method_b)).and_return([subject_b])
|
||||
allow(matcher).to receive(:new).with(env, Foo, Foo.instance_method(:method_c)).and_return([subject_c])
|
||||
end
|
||||
|
||||
it 'should yield expected subjects' do
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Mutant::Matcher::Methods::Singleton, '#each' do
|
||||
let(:object) { described_class.new(cache, Foo) }
|
||||
let(:cache) { Mutant::Cache.new }
|
||||
let(:object) { described_class.new(env, Foo) }
|
||||
let(:env) { Fixtures::BOOT_ENV }
|
||||
|
||||
subject { object.each { |matcher| yields << matcher } }
|
||||
|
||||
|
@ -41,11 +41,11 @@ describe Mutant::Matcher::Methods::Singleton, '#each' do
|
|||
before do
|
||||
matcher = Mutant::Matcher::Method::Singleton
|
||||
matcher.stub(:new)
|
||||
.with(cache, Foo, Foo.method(:method_a)).and_return([subject_a])
|
||||
.with(env, Foo, Foo.method(:method_a)).and_return([subject_a])
|
||||
matcher.stub(:new)
|
||||
.with(cache, Foo, Foo.method(:method_b)).and_return([subject_b])
|
||||
.with(env, Foo, Foo.method(:method_b)).and_return([subject_b])
|
||||
matcher.stub(:new)
|
||||
.with(cache, Foo, Foo.method(:method_c)).and_return([subject_c])
|
||||
.with(env, Foo, Foo.method(:method_c)).and_return([subject_c])
|
||||
end
|
||||
|
||||
it 'should yield expected subjects' do
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Mutant::Matcher::Namespace do
|
||||
let(:object) { described_class.new(cache, Mutant::Expression.parse('TestApp*')) }
|
||||
let(:object) { described_class.new(env, Mutant::Expression.parse('TestApp*')) }
|
||||
let(:yields) { [] }
|
||||
|
||||
let(:cache) { Mutant::Cache.new }
|
||||
let(:env) { Fixtures::BOOT_ENV }
|
||||
|
||||
subject { object.each { |item| yields << item } }
|
||||
|
||||
|
@ -17,11 +16,11 @@ describe Mutant::Matcher::Namespace do
|
|||
let(:subject_b) { double('SubjectB') }
|
||||
|
||||
before do
|
||||
allow(Mutant::Matcher::Methods::Singleton).to receive(:new).with(cache, singleton_a).and_return([subject_a])
|
||||
allow(Mutant::Matcher::Methods::Instance).to receive(:new).with(cache, singleton_a).and_return([])
|
||||
allow(Mutant::Matcher::Methods::Singleton).to receive(:new).with(env, singleton_a).and_return([subject_a])
|
||||
allow(Mutant::Matcher::Methods::Instance).to receive(:new).with(env, singleton_a).and_return([])
|
||||
|
||||
allow(Mutant::Matcher::Methods::Singleton).to receive(:new).with(cache, singleton_b).and_return([subject_b])
|
||||
allow(Mutant::Matcher::Methods::Instance).to receive(:new).with(cache, singleton_b).and_return([])
|
||||
allow(Mutant::Matcher::Methods::Singleton).to receive(:new).with(env, singleton_b).and_return([subject_b])
|
||||
allow(Mutant::Matcher::Methods::Instance).to receive(:new).with(env, singleton_b).and_return([])
|
||||
|
||||
allow(ObjectSpace).to receive(:each_object).with(Module).and_return([singleton_a, singleton_b, singleton_c])
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue