Reduce direct reference of globals

This commit is contained in:
Markus Schirp 2015-11-21 22:02:51 +00:00
parent bb4d4d97b7
commit 9ab35de2c4
21 changed files with 220 additions and 162 deletions

View file

@ -15,7 +15,11 @@ namespace =
load_path: $LOAD_PATH,
kernel: Kernel,
pathname: Pathname,
require_highjack: Mutant::RequireHighjack.method(:call).to_proc.curry.call(Kernel),
require_highjack: Mutant::RequireHighjack
.method(:call)
.to_proc
.curry
.call(Kernel),
root_require: 'mutant',
includes: %w[
mutant

View file

@ -1,3 +1,3 @@
---
threshold: 18
total_score: 1177
total_score: 1173

View file

@ -69,8 +69,8 @@ require 'mutant/parallel/worker'
require 'mutant/parallel/source'
require 'mutant/warning_filter'
require 'mutant/require_highjack'
require 'mutant/mutator'
require 'mutant/mutation'
require 'mutant/mutator'
require 'mutant/mutator/registry'
require 'mutant/mutator/util'
require 'mutant/mutator/util/array'
@ -202,7 +202,11 @@ module Mutant
integration: Integration::Null,
isolation: Mutant::Isolation::Fork,
jobs: ::Parallel.processor_count,
kernel: Kernel,
load_path: $LOAD_PATH,
matcher: Matcher::Config::DEFAULT,
open3: Open3,
pathname: Pathname,
requires: EMPTY_ARRAY,
reporter: Reporter::CLI.build($stdout),
zombie: false

View file

@ -1,26 +1,24 @@
module Mutant
# Commandline parser
#
# rubocop:disable ClassLength
class CLI
include Adamantium::Flat, Equalizer.new(:config), Procto.call(:config)
# Error failed when CLI argv is invalid
Error = Class.new(RuntimeError)
EXIT_FAILURE = 1
EXIT_SUCCESS = 0
# Run cli with arguments
#
# @param [Array<String>] arguments
#
# @return [Fixnum]
# the exit status
# @return [Boolean]
def self.run(arguments)
Runner.call(Env::Bootstrap.call(call(arguments))).success? ? EXIT_SUCCESS : EXIT_FAILURE
Runner.call(Env::Bootstrap.call(call(arguments))).success?
rescue Error => exception
$stderr.puts(exception.message)
EXIT_FAILURE
false
end
# Initialize object
@ -53,7 +51,7 @@ module Mutant
def parse(arguments)
opts = OptionParser.new do |builder|
builder.banner = 'usage: mutant [options] MATCH_EXPRESSION ...'
%w[add_environment_options add_mutation_options add_filter_options add_debug_options].each do |name|
%i[add_environment_options add_mutation_options add_filter_options add_debug_options].each do |name|
__send__(name, builder)
end
end
@ -138,7 +136,16 @@ module Mutant
add_matcher(:ignore_expressions, config.expression_parser.(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)))
add_matcher(
:subject_filters,
Repository::SubjectFilter.new(
Repository::Diff.new(
config: config,
from: Repository::Diff::HEAD,
to: revision
)
)
)
end
end
@ -153,14 +160,14 @@ module Mutant
end
opts.on('--version', 'Print mutants version') do
puts("mutant-#{VERSION}")
Kernel.exit(EXIT_SUCCESS)
config.kernel.exit
end
opts.on('-d', '--debug', 'Enable debugging output') do
with(debug: true)
end
opts.on_tail('-h', '--help', 'Show this message') do
puts(opts.to_s)
Kernel.exit(EXIT_SUCCESS)
config.kernel.exit
end
end

View file

@ -13,7 +13,11 @@ module Mutant
:includes,
:isolation,
:jobs,
:kernel,
:load_path,
:matcher,
:open3,
:pathname,
:requires,
:reporter,
:zombie

View file

@ -44,7 +44,7 @@ module Mutant
tests = selector.call(mutation.subject)
config.isolation.call do
mutation.insert
mutation.insert(config.kernel)
integration.call(tests)
end
rescue Isolation::Error => error

View file

@ -92,8 +92,8 @@ module Mutant
#
# @return [undefined]
def infect
config.includes.each(&$LOAD_PATH.method(:<<))
config.requires.each(&Kernel.method(:require))
config.includes.each(&config.load_path.method(:<<))
config.requires.each(&config.kernel.method(:require))
@integration = config.integration.new(config).setup
end

View file

@ -1,38 +1,27 @@
module Mutant
# Base class for code loaders
class Loader
include AbstractType, Concord.new(:root, :subject), Procto.call
# Eval based loader
class Eval < self
include Anima.new(:binding, :kernel, :node, :subject)
# Call loader
#
# @return [undefined]
# @return [self]
def self.call(*arguments)
new(*arguments).call
end
# Call loader
#
# One off the very few valid uses of eval
#
# rubocop:disable Lint/Eval
# @return [undefined]
def call
eval(
source,
TOPLEVEL_BINDING,
kernel.eval(
Unparser.unparse(node),
binding,
subject.source_path.to_s,
subject.source_line
)
self
end
private
# Source generated from AST
#
# @return [String]
def source
Unparser.unparse(root)
end
end # Eval
end # Loader
end # Mutant

View file

@ -85,7 +85,7 @@ module Mutant
#
# @return [Pathname]
def source_path
Pathname.new(source_location.first)
env.config.pathname.new(source_location.first)
end
memoize :source_path

View file

@ -49,10 +49,17 @@ module Mutant
# Insert mutated node
#
# @param kernel [Kernel]
#
# @return [self]
def insert
def insert(kernel)
subject.prepare
Loader::Eval.call(root, subject)
Loader.call(
binding: TOPLEVEL_BINDING,
kernel: kernel,
node: root,
subject: subject
)
self
end

View file

@ -20,17 +20,9 @@ module Mutant
# Diff between two objects in repository
class Diff
include Adamantium, Concord.new(:from, :to)
include Adamantium, Anima.new(:config, :from, :to)
HEAD = 'HEAD'.freeze
private_constant(*constants(false))
# Create diff from head to revision
#
# @return [Diff]
def self.from_head(to)
new(HEAD, to)
end
# Test if diff changes file at line range
#
@ -50,7 +42,7 @@ module Mutant
-L #{line_range.begin},#{line_range.end}:#{path}
]
stdout, status = Open3.capture2(*command, binmode: true)
stdout, status = config.open3.capture2(*command, binmode: true)
fail RepositoryError, "Command #{command} failed!" unless status.success?
@ -68,7 +60,7 @@ module Mutant
# @return [Boolean]
def tracks?(path)
command = %W[git ls-files --error-unmatch -- #{path}]
Kernel.system(
config.kernel.system(
*command,
out: File::NULL,
err: File::NULL
@ -81,7 +73,7 @@ module Mutant
#
# @return [TrueClass, nil]
def within_working_directory?(path)
working_directory = Pathname.pwd
working_directory = config.pathname.pwd
path.ascend { |parent| return true if working_directory.eql?(parent) }
end

View file

@ -37,12 +37,13 @@ module Mutant
# the last returned status payload
def run_driver(driver)
status = nil
sleep = env.config.kernel.method(:sleep)
loop do
status = driver.status
reporter.progress(status)
break if status.done
Kernel.sleep(reporter.delay)
sleep.call(reporter.delay)
end
driver.stop

View file

@ -33,7 +33,7 @@ RSpec.describe Mutant::CLI do
let(:report_success) { true }
it 'exits failure' do
expect(subject).to be(0)
expect(subject).to be(true)
end
end
@ -41,7 +41,7 @@ RSpec.describe Mutant::CLI do
let(:report_success) { false }
it 'exits failure' do
expect(subject).to be(1)
expect(subject).to be(false)
end
end
@ -55,7 +55,7 @@ RSpec.describe Mutant::CLI do
it 'exits failure' do
expect($stderr).to receive(:puts).with('test-error')
expect(subject).to be(1)
expect(subject).to be(false)
end
end
end
@ -110,7 +110,7 @@ RSpec.describe Mutant::CLI do
before do
expect($stdout).to receive(:puts).with(expected_message)
expect(Kernel).to receive(:exit).with(0)
expect(Kernel).to receive(:exit)
end
it_should_behave_like 'a cli parser'
@ -172,7 +172,7 @@ Options:
let(:flags) { %w[--version] }
before do
expect(Kernel).to receive(:exit).with(0)
expect(Kernel).to receive(:exit)
expect($stdout).to receive(:puts).with("mutant-#{Mutant::VERSION}")
end
@ -237,7 +237,13 @@ Options:
let(:expected_matcher_config) do
default_matcher_config.with(
subject_filters: [
Mutant::Repository::SubjectFilter.new(Mutant::Repository::Diff.new('HEAD', 'master'))
Mutant::Repository::SubjectFilter.new(
Mutant::Repository::Diff.new(
config: Mutant::Config::DEFAULT,
from: 'HEAD',
to: 'master'
)
)
]
)
end

View file

@ -14,13 +14,6 @@ RSpec.describe Mutant::Env do
end
let(:integration) { instance_double(Mutant::Integration) }
let(:config) do
Mutant::Config::DEFAULT.with(isolation: isolation, integration: integration_class)
end
let(:isolation) { instance_double(Mutant::Isolation::Fork.singleton_class) }
let(:mutation) { Mutant::Mutation::Evil.new(mutation_subject, Mutant::AST::Nodes::N_NIL) }
let(:wrapped_node) { instance_double(Parser::AST::Node) }
let(:context) { instance_double(Mutant::Context) }
let(:test_a) { instance_double(Mutant::Test) }
@ -29,6 +22,25 @@ RSpec.describe Mutant::Env do
let(:selector) { instance_double(Mutant::Selector) }
let(:integration_class) { Mutant::Integration::Null }
let(:isolation) do
instance_double(Mutant::Isolation::Fork.singleton_class)
end
let(:mutation) do
instance_double(
Mutant::Mutation,
subject: mutation_subject
)
end
let(:config) do
Mutant::Config::DEFAULT.with(
isolation: isolation,
integration: integration_class,
kernel: instance_double(Kernel.singleton_class)
)
end
let(:mutation_subject) do
instance_double(
Mutant::Subject,
@ -41,7 +53,14 @@ RSpec.describe Mutant::Env do
subject { object.kill(mutation) }
shared_examples_for 'mutation kill' do
it { should eql(Mutant::Result::Mutation.new(mutation: mutation, test_result: test_result)) }
specify do
should eql(
Mutant::Result::Mutation.new(
mutation: mutation,
test_result: test_result
)
)
end
end
before do
@ -53,25 +72,21 @@ RSpec.describe Mutant::Env do
end
context 'when isolation does not raise error' do
let(:test_result) { instance_double(Mutant::Result::Test, passed: false) }
let(:test_result) do
instance_double(
Mutant::Result::Test,
passed: false
)
end
before do
expect(isolation).to receive(:call)
.ordered
.and_yield
expect(mutation_subject).to receive(:prepare)
expect(mutation).to receive(:insert)
.ordered
.and_return(mutation_subject)
expect(context).to receive(:root)
.with(s(:nil))
.and_return(wrapped_node)
expect(Mutant::Loader::Eval).to receive(:call)
.ordered
.with(wrapped_node, mutation_subject)
.and_return(Mutant::Loader::Eval)
.with(config.kernel)
expect(integration).to receive(:call)
.ordered

View file

@ -1,44 +0,0 @@
RSpec.describe Mutant::Loader::Eval, '.call' do
subject { object.call(node, mutation_subject) }
let(:object) { Class.new(described_class) }
let(:path) { __FILE__ }
let(:line) { 1 }
let(:mutation_subject) do
instance_double(Mutant::Subject, source_path: path, source_line: line)
end
let(:source) do
<<-RUBY
class SomeNamespace
class Bar
def some_method
end
end
class SomeOther
class Foo < Bar
end
end
end
RUBY
end
let(:node) do
parse(source)
end
it 'should load nodes into vm' do
subject
::SomeNamespace::SomeOther::Foo
end
it 'should set file and line correctly' do
subject
expect(::SomeNamespace::Bar
.instance_method(:some_method)
.source_location).to eql([__FILE__, 3])
end
end

View file

@ -0,0 +1,42 @@
RSpec.describe Mutant::Loader, '.call' do
subject do
described_class.call(
binding: binding,
kernel: kernel,
node: node,
subject: mutation_subject
)
end
let(:path) { instance_double(Pathname, to_s: path_str) }
let(:path_str) { instance_double(String) }
let(:line) { instance_double(Fixnum) }
let(:kernel) { instance_double(Kernel.singleton_class) }
let(:binding) { instance_double(Binding) }
let(:source) { instance_double(String) }
let(:node) { instance_double(Parser::AST::Node) }
let(:mutation_subject) do
instance_double(
Mutant::Subject,
source_path: path,
source_line: line
)
end
it 'performs expected kernel interaction' do
expect(Unparser).to receive(:unparse)
.with(node)
.and_return(source)
expect(kernel).to receive(:eval)
.with(
source,
binding,
path_str,
line
)
subject
end
end

View file

@ -13,6 +13,7 @@ RSpec.describe Mutant::Matcher::Method::Instance, '#call' do
let(:env) do
instance_double(
Mutant::Env::Bootstrap,
config: Mutant::Config::DEFAULT,
parser: Fixtures::TEST_ENV.parser
)
end

View file

@ -12,6 +12,7 @@ RSpec.describe Mutant::Matcher::Method::Singleton, '#call' do
let(:env) do
instance_double(
Mutant::Env::Bootstrap,
config: Mutant::Config::DEFAULT,
parser: Fixtures::TEST_ENV.parser
)
end

View file

@ -19,9 +19,10 @@ RSpec.describe Mutant::Mutation do
let(:test_b) { instance_double(Mutant::Test) }
describe '#insert' do
subject { object.insert }
subject { object.insert(kernel) }
let(:root_node) { s(:foo) }
let(:kernel) { instance_double(Kernel) }
before do
expect(context).to receive(:root)
@ -32,10 +33,15 @@ RSpec.describe Mutant::Mutation do
.ordered
.and_return(mutation_subject)
expect(Mutant::Loader::Eval).to receive(:call)
expect(Mutant::Loader).to receive(:call)
.ordered
.with(root_node, mutation_subject)
.and_return(Mutant::Loader::Eval)
.with(
binding: TOPLEVEL_BINDING,
kernel: kernel,
node: root_node,
subject: mutation_subject
)
.and_return(Mutant::Loader)
end
it_should_behave_like 'a command method'

View file

@ -1,22 +1,34 @@
describe Mutant::Repository::Diff do
describe '.from_head' do
subject { described_class.from_head(to_revision) }
let(:to_revision) { instance_double(String) }
it { should eql(described_class.new('HEAD', to_revision)) }
describe '#touches?' do
let(:object) do
described_class.new(
config: config,
from: 'from_rev',
to: 'to_rev'
)
end
describe '#touches?' do
let(:object) { described_class.new('from_rev', 'to_rev') }
let(:path) { Pathname.pwd.join('foo.rb') }
let(:config) do
instance_double(
Mutant::Config,
kernel: kernel,
open3: open3,
pathname: pathname
)
end
let(:pathname) { instance_double(Pathname.singleton_class, pwd: pwd) }
let(:open3) { instance_double(Open3.singleton_class) }
let(:kernel) { instance_double(Kernel.singleton_class) }
let(:pwd) { Pathname.new('/foo') }
let(:path) { Pathname.new('/foo/bar.rb') }
let(:line_range) { 1..2 }
subject { object.touches?(path, line_range) }
shared_context 'test if git tracks the file' do
before do
expect(Kernel).to receive(:system)
expect(config.kernel).to receive(:system)
.ordered
.with(
*%W[git ls-files --error-unmatch -- #{path}],
@ -27,10 +39,10 @@ describe Mutant::Repository::Diff do
end
context 'when file is in a different subdirectory' do
let(:path) { Pathname.new('/foo.rb') }
let(:path) { Pathname.new('/baz/bar.rb') }
before do
expect(Kernel).to_not receive(:system)
expect(config.kernel).to_not receive(:system)
end
it { should be(false) }
@ -53,7 +65,7 @@ describe Mutant::Repository::Diff do
include_context 'test if git tracks the file'
before do
expect(Open3).to receive(:capture2)
expect(config.open3).to receive(:capture2)
.ordered
.with(*expected_git_log_command, binmode: true)
.and_return([stdout, status])

View file

@ -4,22 +4,33 @@ RSpec.describe Mutant::Runner do
let(:reporter) { instance_double(Mutant::Reporter, delay: delay) }
let(:driver) { instance_double(Mutant::Parallel::Driver) }
let(:delay) { instance_double(Float) }
let(:env) { instance_double(Mutant::Env, mutations: []) }
let(:env_result) { instance_double(Mutant::Result::Env) }
let(:actor_env) { instance_double(Mutant::Actor::Env) }
let(:kernel) { instance_double(Kernel.singleton_class) }
let(:sleep) { instance_double(Method) }
let(:env) do
instance_double(
Mutant::Env,
actor_env: actor_env,
config: config,
mutations: []
)
end
let(:config) do
instance_double(
Mutant::Config,
integration: integration,
jobs: 1,
kernel: kernel,
reporter: reporter
)
end
before do
allow(env).to receive_messages(config: config, actor_env: actor_env)
allow(env).to receive(:method).with(:kill).and_return(parallel_config.processor)
allow(kernel).to receive(:method).with(:sleep).and_return(sleep)
end
let(:parallel_config) do
@ -57,7 +68,7 @@ RSpec.describe Mutant::Runner do
before do
expect(driver).to receive(:status).and_return(status_a).ordered
expect(reporter).to receive(:progress).with(status_a).ordered
expect(Kernel).to receive(:sleep).with(reporter.delay).ordered
expect(sleep).to receive(:call).with(reporter.delay).ordered
expect(driver).to receive(:status).and_return(status_b).ordered
expect(reporter).to receive(:progress).with(status_b).ordered