Fine grained rspec integration
* Flattens the rspec example groups on filtering to remove redundant work being done on executing subtrees of already executed example groups * Uses test batching to kill one mutation per isolation that results in a significant speedup. * Drop rspec 2 support. [closes #256]
This commit is contained in:
parent
8cbb5e288a
commit
40c337ce5b
22 changed files with 246 additions and 656 deletions
|
@ -1,3 +1,3 @@
|
|||
---
|
||||
threshold: 18
|
||||
total_score: 1179
|
||||
total_score: 1143
|
||||
|
|
|
@ -27,9 +27,7 @@ FeatureEnvy:
|
|||
exclude:
|
||||
- Mutant::Env#scope_name
|
||||
- Mutant::Diff#minimized_hunks
|
||||
- Mutant::Integration::Rspec#run
|
||||
- Mutant::Integration::Rspec::Rspec2#full_description
|
||||
- Mutant::Integration::Rspec::Rspec3#full_description
|
||||
- Mutant::Integration::Rspec#parse_example
|
||||
- Mutant::Matcher::Method::Instance#match?
|
||||
- Mutant::Matcher::Method::Singleton#receiver?
|
||||
- Mutant::Mutator::Node#children_indices
|
||||
|
@ -56,7 +54,6 @@ NestedIterators:
|
|||
exclude:
|
||||
- Mutant#self.singleton_subclass_instance
|
||||
- Mutant::CLI#parse
|
||||
- Mutant::Integration::Rspec#run
|
||||
- Mutant::Isolation::Fork#self.call
|
||||
- Mutant::Mutator::Util::Array::Element#dispatch
|
||||
- Mutant::Mutator::Node::Resbody#mutate_captures
|
||||
|
@ -93,7 +90,6 @@ TooManyMethods:
|
|||
TooManyStatements:
|
||||
enabled: true
|
||||
exclude:
|
||||
- Mutant::Integration::Rspec#run
|
||||
- Mutant::Isolation::Fork#self.call
|
||||
- Mutant::Reporter::CLI::Printer::EnvProgress#run
|
||||
- Mutant::Reporter::CLI::Printer::Config#run
|
||||
|
@ -113,9 +109,7 @@ UncommunicativeMethodName:
|
|||
accept: []
|
||||
UncommunicativeModuleName:
|
||||
enabled: true
|
||||
exclude:
|
||||
- Rspec2
|
||||
- Rspec3
|
||||
exclude: []
|
||||
reject:
|
||||
- !ruby/regexp /^.$/
|
||||
- !ruby/regexp /[0-9]$/
|
||||
|
@ -144,11 +138,7 @@ UtilityFunction:
|
|||
exclude:
|
||||
- Mutant::AST::Sexp#s
|
||||
- Mutant::CLI#reporter
|
||||
- Mutant::Integration::Rspec#configuration
|
||||
- Mutant::Integration::Rspec#options
|
||||
- Mutant::Integration::Rspec::Rspec2#full_description
|
||||
- Mutant::Integration::Rspec::Rspec2#new_reporter
|
||||
- Mutant::Integration::Rspec::Rspec3#full_description
|
||||
- Mutant::Meta::Example::Verification#format_mutation
|
||||
- Mutant::Integration::Rspec#parse_example
|
||||
- Mutant::Meta::Example::Verification#format_mutation # False positive, its a utility
|
||||
- Mutant::Reporter::CLI::Format::Progressive#new_buffer
|
||||
max_helper_calls: 0
|
||||
|
|
|
@ -16,7 +16,7 @@ module Mutant
|
|||
# @api private
|
||||
#
|
||||
def self.lookup(name)
|
||||
REGISTRY.fetch(name).build
|
||||
REGISTRY.fetch(name).new
|
||||
end
|
||||
|
||||
# Register integration
|
||||
|
@ -44,6 +44,16 @@ module Mutant
|
|||
self
|
||||
end
|
||||
|
||||
# Return test result for tests
|
||||
#
|
||||
# @param [Enumerable<Test>] tests
|
||||
#
|
||||
# @return [Result::Test]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
abstract_method :call
|
||||
|
||||
# Return all available tests by integration
|
||||
#
|
||||
# @return [Enumerable<Test>]
|
||||
|
|
|
@ -5,24 +5,25 @@ module Mutant
|
|||
class Integration
|
||||
# Shared parts of rspec2/3 integration
|
||||
class Rspec < self
|
||||
include AbstractType
|
||||
|
||||
ALL = Mutant::Expression.parse('*')
|
||||
EXPRESSION_DELIMITER = ' '.freeze
|
||||
LOCATION_DELIMITER = ':'.freeze
|
||||
EXIT_SUCCESS = 0
|
||||
CLI_OPTIONS = IceNine.deep_freeze(%w[spec --fail-fast])
|
||||
|
||||
register 'rspec'
|
||||
|
||||
RSPEC_2_VERSION_PREFIX = '2.'.freeze
|
||||
|
||||
# Return integration compatible to currently loaded rspec
|
||||
# Initialize rspec integration
|
||||
#
|
||||
# @return [Integration]
|
||||
# @return [undefined]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def self.build
|
||||
if RSpec::Core::Version::STRING.start_with?(RSPEC_2_VERSION_PREFIX)
|
||||
Rspec2.new
|
||||
else
|
||||
Rspec3.new
|
||||
end
|
||||
def initialize
|
||||
@output = StringIO.new
|
||||
@runner = RSpec::Core::Runner.new(RSpec::Core::ConfigurationOptions.new(CLI_OPTIONS))
|
||||
@world = RSpec.world
|
||||
end
|
||||
|
||||
# Setup rspec integration
|
||||
|
@ -32,15 +33,14 @@ module Mutant
|
|||
# @api private
|
||||
#
|
||||
def setup
|
||||
options.configure(configuration)
|
||||
configuration.load_spec_files
|
||||
@runner.setup($stderr, @output)
|
||||
self
|
||||
end
|
||||
memoize :setup
|
||||
|
||||
# Return report for test
|
||||
#
|
||||
# @param [Rspec::Test] test
|
||||
# @param [Enumerable<Mutant::Test>] tests
|
||||
#
|
||||
# @return [Test::Result]
|
||||
#
|
||||
|
@ -48,25 +48,17 @@ module Mutant
|
|||
#
|
||||
# rubocop:disable MethodLength
|
||||
#
|
||||
def run(test)
|
||||
output = StringIO.new
|
||||
failed = false
|
||||
def call(tests)
|
||||
examples = tests.map(&all_tests_index.method(:fetch)).to_set
|
||||
filter_examples(&examples.method(:include?))
|
||||
start = Time.now
|
||||
reporter = new_reporter(output)
|
||||
reporter.report(1) do
|
||||
example_group_index.fetch(test.expression.syntax).each do |example_group|
|
||||
next if example_group.run(reporter)
|
||||
failed = true
|
||||
break
|
||||
end
|
||||
end
|
||||
output.rewind
|
||||
passed = @runner.run_specs(RSpec.world.ordered_example_groups).equal?(EXIT_SUCCESS)
|
||||
@output.rewind
|
||||
Result::Test.new(
|
||||
test: nil,
|
||||
mutation: nil,
|
||||
output: output.read,
|
||||
tests: nil,
|
||||
output: @output.read,
|
||||
runtime: Time.now - start,
|
||||
passed: !failed
|
||||
passed: passed
|
||||
)
|
||||
end
|
||||
|
||||
|
@ -77,136 +69,69 @@ module Mutant
|
|||
# @api private
|
||||
#
|
||||
def all_tests
|
||||
example_group_index.keys.each_with_object([]) do |full_description, aggregate|
|
||||
expression = Expression.try_parse(full_description) or next
|
||||
|
||||
aggregate << Test.new(self, expression)
|
||||
end
|
||||
all_tests_index.keys
|
||||
end
|
||||
memoize :all_tests
|
||||
|
||||
private
|
||||
|
||||
# Return all example groups
|
||||
# Return all tests index
|
||||
#
|
||||
# @return [Hash<String, RSpec::Core::ExampleGroup]
|
||||
# @return [Hash<Test, RSpec::Core::Example]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def example_group_index
|
||||
index = Hash.new { |hash, key| hash[key] = [] }
|
||||
|
||||
RSpec.world.example_groups.flat_map(&:descendants).each do |example_group|
|
||||
full_description = full_description(example_group)
|
||||
index[full_description] << example_group
|
||||
def all_tests_index
|
||||
all_examples.each_with_object({}) do |example, index|
|
||||
index[parse_example(example)] = example
|
||||
end
|
||||
|
||||
index
|
||||
end
|
||||
memoize :example_group_index
|
||||
memoize :all_tests_index
|
||||
|
||||
# Return configuration
|
||||
# Parse example into test
|
||||
#
|
||||
# @return [RSpec::Core::Configuration]
|
||||
# @param [RSpec::Core::Example]
|
||||
#
|
||||
# @return [Test]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def configuration
|
||||
RSpec::Core::Configuration.new
|
||||
end
|
||||
memoize :configuration, freezer: :noop
|
||||
def parse_example(example)
|
||||
metadata = example.metadata
|
||||
location = metadata.fetch(:location)
|
||||
full_description = metadata.fetch(:full_description)
|
||||
expression = Expression.try_parse(full_description.split(EXPRESSION_DELIMITER, 2).first) || ALL
|
||||
|
||||
# Return options
|
||||
Test.new(
|
||||
id: "rspec:#{location} / #{full_description}",
|
||||
expression: expression
|
||||
)
|
||||
end
|
||||
|
||||
# Return all examples
|
||||
#
|
||||
# @return [RSpec::Core::ConfigurationOptions]
|
||||
# @return [Array<String, RSpec::Core::Example]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def options
|
||||
RSpec::Core::ConfigurationOptions.new(%w[--fail-fast spec])
|
||||
def all_examples
|
||||
@world.example_groups.flat_map(&:descendants).flat_map(&:examples)
|
||||
end
|
||||
memoize :options, freezer: :noop
|
||||
|
||||
# Rspec2 integration
|
||||
class Rspec2 < self
|
||||
|
||||
register 'rspec'
|
||||
|
||||
private
|
||||
|
||||
# Return options
|
||||
#
|
||||
# @return [RSpec::Core::ConfigurationOptions]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def options
|
||||
super.tap(&:parse_options)
|
||||
# Filter examples
|
||||
#
|
||||
# @param [#call] predicate
|
||||
#
|
||||
# @return [undefined]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def filter_examples(&predicate)
|
||||
@world.filtered_examples.each_value do |examples|
|
||||
examples.keep_if(&predicate)
|
||||
end
|
||||
end
|
||||
|
||||
# Return full description of example group
|
||||
#
|
||||
# @param [RSpec::Core::ExampleGroup] example_group
|
||||
#
|
||||
# @return [String]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def full_description(example_group)
|
||||
example_group.metadata.fetch(:example_group).fetch(:full_description)
|
||||
end
|
||||
|
||||
# Return new reporter
|
||||
#
|
||||
# @param [StringIO] output
|
||||
#
|
||||
# @return [RSpec::Core::Reporter]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def new_reporter(output)
|
||||
formatter = RSpec::Core::Formatters::BaseTextFormatter.new(output)
|
||||
|
||||
RSpec::Core::Reporter.new(formatter)
|
||||
end
|
||||
|
||||
end # Rspec2
|
||||
|
||||
# Rspec 3 integration
|
||||
class Rspec3 < self
|
||||
|
||||
private
|
||||
|
||||
# Return full description for example group
|
||||
#
|
||||
# @param [RSpec::Core::ExampleGroup] example_group
|
||||
#
|
||||
# @return [String]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def full_description(example_group)
|
||||
example_group.metadata.fetch(:full_description)
|
||||
end
|
||||
|
||||
# Return new reporter
|
||||
#
|
||||
# @param [StringIO] output
|
||||
#
|
||||
# @return [RSpec::Core::Reporter]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def new_reporter(output)
|
||||
formatter = RSpec::Core::Formatters::BaseTextFormatter.new(output)
|
||||
notifications = RSpec::Core::Formatters::Loader.allocate.send(:notifications_for, formatter.class)
|
||||
|
||||
RSpec::Core::Reporter.new(configuration).tap do |reporter|
|
||||
reporter.register_listener(formatter, *notifications)
|
||||
end
|
||||
end
|
||||
|
||||
end # Rspec3
|
||||
end # Rspec
|
||||
end # Integration
|
||||
end # Mutant
|
||||
|
|
|
@ -7,45 +7,30 @@ module Mutant
|
|||
CODE_DELIMITER = "\0".freeze
|
||||
CODE_RANGE = (0..4).freeze
|
||||
|
||||
# Kill mutation via isolation
|
||||
# Kill mutation under isolation with integration
|
||||
#
|
||||
# @param [Isolation] isolation
|
||||
# @param [Integration] integration
|
||||
#
|
||||
# @return [Result::Mutation]
|
||||
# @return [Result::Test]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def kill(isolation)
|
||||
result = Result::Mutation.new(
|
||||
index: nil,
|
||||
mutation: self,
|
||||
test_results: []
|
||||
def kill(isolation, integration)
|
||||
start = Time.now
|
||||
tests = subject.tests
|
||||
|
||||
isolation.call do
|
||||
insert
|
||||
integration.call(tests)
|
||||
end.update(tests: tests)
|
||||
rescue Isolation::Error => error
|
||||
Result::Test.new(
|
||||
tests: tests,
|
||||
output: error.message,
|
||||
runtime: Time.now - start,
|
||||
passed: false
|
||||
)
|
||||
|
||||
subject.tests.reduce(result) do |current, test|
|
||||
return current unless current.continue?
|
||||
test_result = test.kill(isolation, self)
|
||||
current.update(
|
||||
test_results: current.test_results.dup << test_result
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
# Insert mutated node
|
||||
#
|
||||
# FIXME: Cache subject visibility in a better way! Ideally dont mutate it
|
||||
# implicitly. Also subject.public? should NOT be a public interface it
|
||||
# is a detail of method mutations.
|
||||
#
|
||||
# @return [self]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def insert
|
||||
subject.public?
|
||||
subject.prepare
|
||||
Loader::Eval.call(root, subject)
|
||||
self
|
||||
end
|
||||
|
||||
# Return identification
|
||||
|
@ -99,17 +84,28 @@ module Mutant
|
|||
#
|
||||
# @api private
|
||||
#
|
||||
abstract_singleton_method :success?
|
||||
def self.success?(test_result)
|
||||
self::TEST_PASS_SUCCESS.equal?(test_result.passed)
|
||||
end
|
||||
|
||||
# Test if execution can be continued
|
||||
private
|
||||
|
||||
# Insert mutated node
|
||||
#
|
||||
# @return [Boolean]
|
||||
# FIXME: Cache subject visibility in a better way! Ideally dont mutate it
|
||||
# implicitly. Also subject.public? should NOT be a public interface it
|
||||
# is a detail of method mutations.
|
||||
#
|
||||
# @return [self]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
abstract_singleton_method :continue?
|
||||
|
||||
private
|
||||
def insert
|
||||
subject.public?
|
||||
subject.prepare
|
||||
Loader::Eval.call(root, subject)
|
||||
self
|
||||
end
|
||||
|
||||
# Return sha1 sum of source and subject identification
|
||||
#
|
||||
|
@ -135,65 +131,24 @@ module Mutant
|
|||
# Evil mutation that should case mutations to fail tests
|
||||
class Evil < self
|
||||
|
||||
SYMBOL = 'evil'.freeze
|
||||
|
||||
# Test if mutation is killed by test reports
|
||||
#
|
||||
# @param [Array<Report::Test>] test_reports
|
||||
#
|
||||
# @return [Boolean]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def self.success?(test_results)
|
||||
!test_results.all?(&:passed)
|
||||
end
|
||||
|
||||
# Test if mutation execution can be continued
|
||||
#
|
||||
# @return [Boolean]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def self.continue?(test_results)
|
||||
!success?(test_results)
|
||||
end
|
||||
SYMBOL = 'evil'.freeze
|
||||
TEST_PASS_SUCCESS = false
|
||||
|
||||
end # Evil
|
||||
|
||||
# Neutral mutation that should not cause mutations to fail tests
|
||||
class Neutral < self
|
||||
|
||||
SYMBOL = 'neutral'.freeze
|
||||
|
||||
# Test if mutation is killed by test reports
|
||||
#
|
||||
# @param [Array<Report::Test>] test_reports
|
||||
#
|
||||
# @return [Boolean]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def self.success?(test_results)
|
||||
test_results.any? && test_results.all?(&:passed)
|
||||
end
|
||||
|
||||
# Test if mutation execution can be continued
|
||||
#
|
||||
# @return [Boolean] _test_results
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def self.continue?(_test_results)
|
||||
true
|
||||
end
|
||||
SYMBOL = 'neutral'.freeze
|
||||
TEST_PASS_SUCCESS = true
|
||||
|
||||
end # Neutral
|
||||
|
||||
# Noop mutation, special case of neutral
|
||||
class Noop < Neutral
|
||||
|
||||
SYMBOL = 'noop'.freeze
|
||||
SYMBOL = 'noop'.freeze
|
||||
TEST_PASS_SUCCESS = true
|
||||
|
||||
end # Noop
|
||||
|
||||
|
|
|
@ -430,7 +430,7 @@ module Mutant
|
|||
# Reporter for mutation results
|
||||
class MutationResult < self
|
||||
|
||||
delegate :mutation, :failed_test_results
|
||||
delegate :mutation, :test_result
|
||||
|
||||
DIFF_ERROR_MESSAGE = 'BUG: Mutation NOT resulted in exactly one diff hunk. Please report a reproduction!'.freeze
|
||||
|
||||
|
@ -448,13 +448,13 @@ module Mutant
|
|||
"%s\n" \
|
||||
"Unparsed Source:\n" \
|
||||
"%s\n" \
|
||||
"Test Reports: %d\n"
|
||||
"Test Result:\n".freeze
|
||||
|
||||
NOOP_MESSAGE =
|
||||
"---- Noop failure -----\n" \
|
||||
"No code was inserted. And the test did NOT PASS.\n" \
|
||||
"This is typically a problem of your specs not passing unmutated.\n" \
|
||||
"Test Reports: %d\n"
|
||||
"Test Result:\n".freeze
|
||||
|
||||
FOOTER = '-----------------------'.freeze
|
||||
|
||||
|
@ -503,8 +503,8 @@ module Mutant
|
|||
# @api private
|
||||
#
|
||||
def noop_details
|
||||
info(NOOP_MESSAGE, failed_test_results.length)
|
||||
visit_failed_test_results
|
||||
info(NOOP_MESSAGE)
|
||||
visit_test_result
|
||||
end
|
||||
|
||||
# Neutral details
|
||||
|
@ -514,8 +514,8 @@ module Mutant
|
|||
# @api private
|
||||
#
|
||||
def neutral_details
|
||||
info(NEUTRAL_MESSAGE, mutation.subject.node.inspect, mutation.source, failed_test_results.length)
|
||||
visit_failed_test_results
|
||||
info(NEUTRAL_MESSAGE, mutation.subject.node.inspect, mutation.source)
|
||||
visit_test_result
|
||||
end
|
||||
|
||||
# Visit failed test results
|
||||
|
@ -524,8 +524,8 @@ module Mutant
|
|||
#
|
||||
# @api private
|
||||
#
|
||||
def visit_failed_test_results
|
||||
visit_collection(TestResult, failed_test_results)
|
||||
def visit_test_result
|
||||
visit(TestResult, test_result)
|
||||
end
|
||||
|
||||
end # MutationResult
|
||||
|
@ -533,7 +533,7 @@ module Mutant
|
|||
# Test result reporter
|
||||
class TestResult < self
|
||||
|
||||
delegate :test, :runtime, :mutation
|
||||
delegate :tests, :runtime
|
||||
|
||||
# Run test result reporter
|
||||
#
|
||||
|
@ -542,7 +542,10 @@ module Mutant
|
|||
# @api private
|
||||
#
|
||||
def run
|
||||
status('- %s / runtime: %s', test.identification, runtime)
|
||||
status('- %d @ runtime: %s', tests.length, runtime)
|
||||
tests.each do |test|
|
||||
puts(" - #{test.identification}")
|
||||
end
|
||||
puts('Test Output:')
|
||||
puts(object.output)
|
||||
end
|
||||
|
|
|
@ -148,21 +148,11 @@ module Mutant
|
|||
# Test result
|
||||
class Test
|
||||
include Result, Anima.new(
|
||||
:test,
|
||||
:tests,
|
||||
:output,
|
||||
:mutation,
|
||||
:passed,
|
||||
:runtime
|
||||
)
|
||||
|
||||
# Return killtime
|
||||
#
|
||||
# @return [Float]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
alias_method :killtime, :runtime
|
||||
|
||||
end # Test
|
||||
|
||||
# Subject result
|
||||
|
@ -258,20 +248,19 @@ module Mutant
|
|||
|
||||
# Mutation result
|
||||
class Mutation
|
||||
include Result, Anima.new(:mutation, :test_results, :index)
|
||||
include Result, Anima.new(:mutation, :test_result, :index)
|
||||
|
||||
sum :runtime, :test_results
|
||||
|
||||
# Return failed test results
|
||||
# Return runtime
|
||||
#
|
||||
# @return [Array<Result::Test>]
|
||||
# @return [Float]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def failed_test_results
|
||||
test_results.reject(&:passed)
|
||||
def runtime
|
||||
test_result.runtime
|
||||
end
|
||||
memoize :failed_test_results
|
||||
|
||||
alias_method :killtime, :runtime
|
||||
|
||||
# Test if mutation was handled successfully
|
||||
#
|
||||
|
@ -280,22 +269,9 @@ module Mutant
|
|||
# @api private
|
||||
#
|
||||
def success?
|
||||
mutation.class.success?(test_results)
|
||||
mutation.class.success?(test_result)
|
||||
end
|
||||
|
||||
# Test if execution on mutation can be stopped
|
||||
#
|
||||
# @return [Boolean]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def continue?
|
||||
mutation.class.continue?(test_results)
|
||||
end
|
||||
|
||||
sum :killtime, :test_results
|
||||
|
||||
end # Mutation
|
||||
|
||||
end # Result
|
||||
end # Mutant
|
||||
|
|
|
@ -79,8 +79,12 @@ module Mutant
|
|||
# @api private
|
||||
#
|
||||
def run_mutation(job)
|
||||
job.mutation.kill(config.isolation).update(
|
||||
index: job.index
|
||||
mutation = job.mutation
|
||||
test_result = mutation.kill(config.isolation, config.integration)
|
||||
Result::Mutation.new(
|
||||
index: job.index,
|
||||
mutation: mutation,
|
||||
test_result: test_result
|
||||
)
|
||||
end
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
module Mutant
|
||||
# Abstract base class for test that might kill a mutation
|
||||
class Test
|
||||
include Adamantium::Flat, Concord::Public.new(:integration, :expression)
|
||||
include Adamantium::Flat, Anima.new(:id, :expression)
|
||||
|
||||
# Return test identification
|
||||
#
|
||||
|
@ -9,47 +9,7 @@ module Mutant
|
|||
#
|
||||
# @api private
|
||||
#
|
||||
def identification
|
||||
"#{integration.name}:#{expression.syntax}"
|
||||
end
|
||||
memoize :identification
|
||||
|
||||
# Kill mutation with test under isolation
|
||||
#
|
||||
# @param [Isolation] isolation
|
||||
# @param [Mutation] mutation
|
||||
#
|
||||
# @return [Report::Test]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def kill(isolation, mutation)
|
||||
time = Time.now
|
||||
isolation.call do
|
||||
mutation.insert
|
||||
run
|
||||
end.update(test: self)
|
||||
rescue Isolation::Error => exception
|
||||
Result::Test.new(
|
||||
test: self,
|
||||
mutation: mutation,
|
||||
runtime: Time.now - time,
|
||||
output: exception.message,
|
||||
passed: false
|
||||
)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Run test, return report
|
||||
#
|
||||
# @return [Report]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def run
|
||||
integration.run(self)
|
||||
end
|
||||
alias_method :identification, :id
|
||||
|
||||
end # Test
|
||||
end # Mutant
|
||||
|
|
|
@ -38,12 +38,6 @@ RSpec.describe 'rspec integration' do
|
|||
end
|
||||
end
|
||||
|
||||
context 'RSpec 2' do
|
||||
let(:gemfile) { 'Gemfile.rspec2' }
|
||||
|
||||
it_behaves_like 'rspec integration'
|
||||
end
|
||||
|
||||
context 'RSpec 3.0' do
|
||||
let(:gemfile) { 'Gemfile.rspec3.0' }
|
||||
|
||||
|
|
|
@ -85,57 +85,35 @@ module SharedContext
|
|||
|
||||
let(:mutation_a_result) do
|
||||
Mutant::Result::Mutation.new(
|
||||
index: 1,
|
||||
mutation: mutation_a,
|
||||
test_results: [mutation_a_test_a_result, mutation_a_test_b_result]
|
||||
index: 1,
|
||||
mutation: mutation_a,
|
||||
test_result: mutation_a_test_result
|
||||
)
|
||||
end
|
||||
|
||||
let(:mutation_b_result) do
|
||||
Mutant::Result::Mutation.new(
|
||||
index: 1,
|
||||
mutation: mutation_a,
|
||||
test_results: [mutation_b_test_a_result, mutation_b_test_b_result]
|
||||
index: 1,
|
||||
mutation: mutation_a,
|
||||
test_result: mutation_b_test_result
|
||||
)
|
||||
end
|
||||
|
||||
let(:mutation_a_test_a_result) do
|
||||
let(:mutation_a_test_result) do
|
||||
Mutant::Result::Test.new(
|
||||
mutation: mutation_a,
|
||||
test: test_a,
|
||||
tests: [test_a],
|
||||
passed: false,
|
||||
runtime: 1.0,
|
||||
output: 'mutation a test a result output'
|
||||
output: 'mutation a test result output'
|
||||
)
|
||||
end
|
||||
|
||||
let(:mutation_a_test_b_result) do
|
||||
let(:mutation_b_test_result) do
|
||||
Mutant::Result::Test.new(
|
||||
mutation: mutation_b,
|
||||
test: test_b,
|
||||
tests: [test_a],
|
||||
passed: false,
|
||||
runtime: 1.0,
|
||||
output: 'mutation a test b result output'
|
||||
)
|
||||
end
|
||||
|
||||
let(:mutation_b_test_a_result) do
|
||||
Mutant::Result::Test.new(
|
||||
mutation: mutation_b,
|
||||
test: test_a,
|
||||
passed: false,
|
||||
runtime: 1.0,
|
||||
output: 'mutation b test a result output'
|
||||
)
|
||||
end
|
||||
|
||||
let(:mutation_b_test_b_result) do
|
||||
Mutant::Result::Test.new(
|
||||
mutation: mutation_b,
|
||||
test: test_b,
|
||||
passed: false,
|
||||
runtime: 1.0,
|
||||
output: 'mutation b test b result output'
|
||||
output: 'mutation b test result output'
|
||||
)
|
||||
end
|
||||
|
||||
|
|
|
@ -160,7 +160,7 @@ Options:
|
|||
|
||||
it_should_behave_like 'a cli parser'
|
||||
|
||||
let(:expected_integration) { Mutant::Integration::Rspec.build }
|
||||
let(:expected_integration) { Mutant::Integration::Rspec.new }
|
||||
end
|
||||
|
||||
context 'when integration does NOT exist' do
|
||||
|
|
|
@ -1,63 +0,0 @@
|
|||
RSpec.describe Mutant::Mutation::Evil do
|
||||
let(:object) do
|
||||
described_class.new(mutation_subject, double('node'))
|
||||
end
|
||||
|
||||
let(:mutation_subject) { double('subject') }
|
||||
|
||||
describe '.continue?' do
|
||||
subject { described_class.continue?(test_results) }
|
||||
|
||||
context 'with empty test results' do
|
||||
let(:test_results) { [] }
|
||||
|
||||
it { should be(true) }
|
||||
end
|
||||
|
||||
context 'with single passed test result' do
|
||||
let(:test_results) { [double('test result', passed: true)] }
|
||||
|
||||
it { should be(true) }
|
||||
end
|
||||
|
||||
context 'with failed test result' do
|
||||
let(:test_results) { [double('test result', passed: false)] }
|
||||
|
||||
it { should be(false) }
|
||||
end
|
||||
|
||||
context 'with passed test result and failed test result' do
|
||||
let(:test_results) { [double('test result', passed: true), double('test result', passed: false)] }
|
||||
|
||||
it { should be(false) }
|
||||
end
|
||||
end
|
||||
|
||||
describe '.success?' do
|
||||
subject { described_class.success?(test_results) }
|
||||
|
||||
context 'with empty test results' do
|
||||
let(:test_results) { [] }
|
||||
|
||||
it { should be(false) }
|
||||
end
|
||||
|
||||
context 'with single passed test result' do
|
||||
let(:test_results) { [double('test result', passed: true)] }
|
||||
|
||||
it { should be(false) }
|
||||
end
|
||||
|
||||
context 'with failed test result' do
|
||||
let(:test_results) { [double('test result', passed: false)] }
|
||||
|
||||
it { should be(true) }
|
||||
end
|
||||
|
||||
context 'with passed test result and failed test result' do
|
||||
let(:test_results) { [double('test result', passed: true), double('test result', passed: false)] }
|
||||
|
||||
it { should be(true) }
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,63 +0,0 @@
|
|||
RSpec.describe Mutant::Mutation::Neutral do
|
||||
let(:object) do
|
||||
described_class.new(mutation_subject, double('node'))
|
||||
end
|
||||
|
||||
let(:mutation_subject) { double('subject') }
|
||||
|
||||
describe '.continue?' do
|
||||
subject { described_class.continue?(test_results) }
|
||||
|
||||
context 'with empty test results' do
|
||||
let(:test_results) { [] }
|
||||
|
||||
it { should be(true) }
|
||||
end
|
||||
|
||||
context 'with single passed test result' do
|
||||
let(:test_results) { [double('test result', passed: true)] }
|
||||
|
||||
it { should be(true) }
|
||||
end
|
||||
|
||||
context 'with failed test result' do
|
||||
let(:test_results) { [double('test result', passed: false)] }
|
||||
|
||||
it { should be(true) }
|
||||
end
|
||||
|
||||
context 'with passed test result and failed test result' do
|
||||
let(:test_results) { [double('test result', passed: true), double('test result', passed: false)] }
|
||||
|
||||
it { should be(true) }
|
||||
end
|
||||
end
|
||||
|
||||
describe '.success?' do
|
||||
subject { described_class.success?(test_results) }
|
||||
|
||||
context 'with empty test results' do
|
||||
let(:test_results) { [] }
|
||||
|
||||
it { should be(false) }
|
||||
end
|
||||
|
||||
context 'with single passed test result' do
|
||||
let(:test_results) { [double('test result', passed: true)] }
|
||||
|
||||
it { should be(true) }
|
||||
end
|
||||
|
||||
context 'with failed test result' do
|
||||
let(:test_results) { [double('test result', passed: false)] }
|
||||
|
||||
it { should be(false) }
|
||||
end
|
||||
|
||||
context 'with passed test result and failed test result' do
|
||||
let(:test_results) { [double('test result', passed: true), double('test result', passed: false)] }
|
||||
|
||||
it { should be(false) }
|
||||
end
|
||||
end
|
||||
end
|
|
@ -13,26 +13,48 @@ RSpec.describe Mutant::Mutation do
|
|||
identification: 'subject',
|
||||
context: context,
|
||||
source: 'original',
|
||||
tests: [test_a, test_b]
|
||||
tests: tests
|
||||
)
|
||||
end
|
||||
|
||||
let(:test_a) { double('Test A') }
|
||||
let(:test_b) { double('Test B') }
|
||||
let(:tests) { [test_a, test_b] }
|
||||
|
||||
describe '#kill' do
|
||||
let(:isolation) { double('Isolation') }
|
||||
let(:object) { Mutant::Mutation::Evil.new(mutation_subject, Mutant::AST::Nodes::N_NIL) }
|
||||
let(:isolation) { Mutant::Isolation::None }
|
||||
let(:integration) { double('Integration') }
|
||||
let(:object) { Mutant::Mutation::Evil.new(mutation_subject, Mutant::AST::Nodes::N_NIL) }
|
||||
let(:wrapped_node) { double('Wrapped Node') }
|
||||
|
||||
let(:test_result_a) { double('Test Result A', passed: false) }
|
||||
subject { object.kill(isolation, integration) }
|
||||
|
||||
before do
|
||||
expect(test_a).to receive(:kill).with(isolation, object).and_return(test_result_a)
|
||||
allow(Time).to receive(:now).and_return(Time.at(0))
|
||||
end
|
||||
|
||||
subject { object.kill(isolation) }
|
||||
context 'when isolation does not raise error' do
|
||||
let(:test_result) { double('Test Result A', passed: false) }
|
||||
|
||||
it { should eql(Mutant::Result::Mutation.new(index: nil, mutation: object, test_results: [test_result_a])) }
|
||||
before do
|
||||
expect(mutation_subject).to receive(:public?).and_return(true).ordered
|
||||
expect(mutation_subject).to receive(:prepare).and_return(mutation_subject).ordered
|
||||
expect(context).to receive(:root).with(s(:nil)).and_return(wrapped_node).ordered
|
||||
expect(Mutant::Loader::Eval).to receive(:call).with(wrapped_node, mutation_subject).and_return(nil).ordered
|
||||
expect(integration).to receive(:call).with(tests).and_return(test_result).ordered
|
||||
expect(test_result).to receive(:update).with(tests: tests).and_return(test_result).ordered
|
||||
end
|
||||
|
||||
it { should eql(test_result) }
|
||||
end
|
||||
|
||||
context 'when isolation does raise error' do
|
||||
before do
|
||||
expect(isolation).to receive(:call).and_raise(Mutant::Isolation::Error, 'test-error')
|
||||
end
|
||||
|
||||
it { should eql(Mutant::Result::Test.new(tests: tests, output: 'test-error', passed: false, runtime: 0.0)) }
|
||||
end
|
||||
end
|
||||
|
||||
describe '#code' do
|
||||
|
@ -51,21 +73,6 @@ RSpec.describe Mutant::Mutation do
|
|||
it_should_behave_like 'an idempotent method'
|
||||
end
|
||||
|
||||
describe '#insert' do
|
||||
subject { object.insert }
|
||||
|
||||
let(:wrapped_node) { double('Wrapped Node') }
|
||||
|
||||
before do
|
||||
expect(mutation_subject).to receive(:public?).ordered.and_return(true)
|
||||
expect(mutation_subject).to receive(:prepare).ordered
|
||||
expect(context).to receive(:root).ordered.with(s(:nil)).and_return(wrapped_node)
|
||||
expect(Mutant::Loader::Eval).to receive(:call).ordered.with(wrapped_node, mutation_subject).and_return(nil)
|
||||
end
|
||||
|
||||
it_should_behave_like 'a command method'
|
||||
end
|
||||
|
||||
describe '#source' do
|
||||
subject { object.source }
|
||||
|
||||
|
|
|
@ -127,8 +127,7 @@ RSpec.describe Mutant::Reporter::CLI do
|
|||
end
|
||||
|
||||
context 'when mutation is NOT successful' do
|
||||
update(:mutation_a_test_a_result) { { passed: true } }
|
||||
update(:mutation_a_test_b_result) { { passed: true } }
|
||||
update(:mutation_a_test_result) { { passed: true } }
|
||||
it_reports 'F.'
|
||||
end
|
||||
end
|
||||
|
@ -178,8 +177,8 @@ RSpec.describe Mutant::Reporter::CLI do
|
|||
Kills: 2
|
||||
Alive: 0
|
||||
Runtime: 4.00s
|
||||
Killtime: 4.00s
|
||||
Overhead: 0.00%
|
||||
Killtime: 2.00s
|
||||
Overhead: 100.00%
|
||||
Coverage: 100.00%
|
||||
Expected: 100.00%
|
||||
Active subjects: 0
|
||||
|
@ -190,8 +189,7 @@ RSpec.describe Mutant::Reporter::CLI do
|
|||
update(:status) { { active_jobs: [job_a].to_set } }
|
||||
|
||||
context 'on failure' do
|
||||
update(:mutation_a_test_a_result) { { passed: true } }
|
||||
update(:mutation_a_test_b_result) { { passed: true } }
|
||||
update(:mutation_a_test_result) { { passed: true } }
|
||||
|
||||
it_reports(<<-REPORT)
|
||||
Mutant configuration:
|
||||
|
@ -207,15 +205,15 @@ RSpec.describe Mutant::Reporter::CLI do
|
|||
Kills: 1
|
||||
Alive: 1
|
||||
Runtime: 4.00s
|
||||
Killtime: 4.00s
|
||||
Overhead: 0.00%
|
||||
Killtime: 2.00s
|
||||
Overhead: 100.00%
|
||||
Coverage: 50.00%
|
||||
Expected: 100.00%
|
||||
Active subjects: 1
|
||||
subject-a mutations: 2
|
||||
- test-a
|
||||
F.
|
||||
(01/02) 50% - killtime: 4.00s runtime: 4.00s overhead: 0.00s
|
||||
(01/02) 50% - killtime: 2.00s runtime: 2.00s overhead: 0.00s
|
||||
Active Jobs:
|
||||
0: evil:subject-a:d27d2
|
||||
REPORT
|
||||
|
@ -236,15 +234,15 @@ RSpec.describe Mutant::Reporter::CLI do
|
|||
Kills: 2
|
||||
Alive: 0
|
||||
Runtime: 4.00s
|
||||
Killtime: 4.00s
|
||||
Overhead: 0.00%
|
||||
Killtime: 2.00s
|
||||
Overhead: 100.00%
|
||||
Coverage: 100.00%
|
||||
Expected: 100.00%
|
||||
Active subjects: 1
|
||||
subject-a mutations: 2
|
||||
- test-a
|
||||
..
|
||||
(02/02) 100% - killtime: 4.00s runtime: 4.00s overhead: 0.00s
|
||||
(02/02) 100% - killtime: 2.00s runtime: 2.00s overhead: 0.00s
|
||||
Active Jobs:
|
||||
0: evil:subject-a:d27d2
|
||||
REPORT
|
||||
|
@ -271,16 +269,15 @@ RSpec.describe Mutant::Reporter::CLI do
|
|||
Kills: 2
|
||||
Alive: 0
|
||||
Runtime: 4.00s
|
||||
Killtime: 4.00s
|
||||
Overhead: 0.00%
|
||||
Killtime: 2.00s
|
||||
Overhead: 100.00%
|
||||
Coverage: 100.00%
|
||||
Expected: 100.00%
|
||||
REPORT
|
||||
end
|
||||
|
||||
context 'and partial coverage' do
|
||||
update(:mutation_a_test_a_result) { { passed: true } }
|
||||
update(:mutation_a_test_b_result) { { passed: true } }
|
||||
update(:mutation_a_test_result) { { passed: true } }
|
||||
|
||||
context 'on evil mutation' do
|
||||
context 'with a diff' do
|
||||
|
@ -305,8 +302,8 @@ RSpec.describe Mutant::Reporter::CLI do
|
|||
Kills: 1
|
||||
Alive: 1
|
||||
Runtime: 4.00s
|
||||
Killtime: 4.00s
|
||||
Overhead: 0.00%
|
||||
Killtime: 2.00s
|
||||
Overhead: 100.00%
|
||||
Coverage: 50.00%
|
||||
Expected: 100.00%
|
||||
REPORT
|
||||
|
@ -338,8 +335,8 @@ RSpec.describe Mutant::Reporter::CLI do
|
|||
Kills: 1
|
||||
Alive: 1
|
||||
Runtime: 4.00s
|
||||
Killtime: 4.00s
|
||||
Overhead: 0.00%
|
||||
Killtime: 2.00s
|
||||
Overhead: 100.00%
|
||||
Coverage: 50.00%
|
||||
Expected: 100.00%
|
||||
REPORT
|
||||
|
@ -347,8 +344,7 @@ RSpec.describe Mutant::Reporter::CLI do
|
|||
end
|
||||
|
||||
context 'on neutral mutation' do
|
||||
update(:mutation_a_test_a_result) { { passed: false } }
|
||||
update(:mutation_a_test_b_result) { { passed: false } }
|
||||
update(:mutation_a_test_result) { { passed: false } }
|
||||
|
||||
let(:mutation_a) do
|
||||
Mutant::Mutation::Neutral.new(subject_a, s(:true))
|
||||
|
@ -365,13 +361,11 @@ RSpec.describe Mutant::Reporter::CLI do
|
|||
(true)
|
||||
Unparsed Source:
|
||||
true
|
||||
Test Reports: 2
|
||||
- test-a / runtime: 1.0
|
||||
Test Result:
|
||||
- 1 @ runtime: 1.0
|
||||
- test-a
|
||||
Test Output:
|
||||
mutation a test a result output
|
||||
- test-b / runtime: 1.0
|
||||
Test Output:
|
||||
mutation a test b result output
|
||||
mutation a test result output
|
||||
-----------------------
|
||||
neutral:subject-a:d5318
|
||||
--- Neutral failure ---
|
||||
|
@ -381,13 +375,11 @@ RSpec.describe Mutant::Reporter::CLI do
|
|||
(true)
|
||||
Unparsed Source:
|
||||
true
|
||||
Test Reports: 2
|
||||
- test-a / runtime: 1.0
|
||||
Test Result:
|
||||
- 1 @ runtime: 1.0
|
||||
- test-a
|
||||
Test Output:
|
||||
mutation b test a result output
|
||||
- test-b / runtime: 1.0
|
||||
Test Output:
|
||||
mutation b test b result output
|
||||
mutation b test result output
|
||||
-----------------------
|
||||
Mutant configuration:
|
||||
Matcher: #<Mutant::Matcher::Config match_expressions=[] subject_ignores=[] subject_selects=[]>
|
||||
|
@ -402,16 +394,15 @@ RSpec.describe Mutant::Reporter::CLI do
|
|||
Kills: 0
|
||||
Alive: 2
|
||||
Runtime: 4.00s
|
||||
Killtime: 4.00s
|
||||
Overhead: 0.00%
|
||||
Killtime: 2.00s
|
||||
Overhead: 100.00%
|
||||
Coverage: 0.00%
|
||||
Expected: 100.00%
|
||||
REPORT
|
||||
end
|
||||
|
||||
context 'on noop mutation' do
|
||||
update(:mutation_a_test_a_result) { { passed: false } }
|
||||
update(:mutation_a_test_b_result) { { passed: false } }
|
||||
update(:mutation_a_test_result) { { passed: false } }
|
||||
|
||||
let(:mutation_a) do
|
||||
Mutant::Mutation::Noop.new(subject_a, s(:true))
|
||||
|
@ -424,25 +415,21 @@ RSpec.describe Mutant::Reporter::CLI do
|
|||
---- Noop failure -----
|
||||
No code was inserted. And the test did NOT PASS.
|
||||
This is typically a problem of your specs not passing unmutated.
|
||||
Test Reports: 2
|
||||
- test-a / runtime: 1.0
|
||||
Test Result:
|
||||
- 1 @ runtime: 1.0
|
||||
- test-a
|
||||
Test Output:
|
||||
mutation a test a result output
|
||||
- test-b / runtime: 1.0
|
||||
Test Output:
|
||||
mutation a test b result output
|
||||
mutation a test result output
|
||||
-----------------------
|
||||
noop:subject-a:d5318
|
||||
---- Noop failure -----
|
||||
No code was inserted. And the test did NOT PASS.
|
||||
This is typically a problem of your specs not passing unmutated.
|
||||
Test Reports: 2
|
||||
- test-a / runtime: 1.0
|
||||
Test Result:
|
||||
- 1 @ runtime: 1.0
|
||||
- test-a
|
||||
Test Output:
|
||||
mutation b test a result output
|
||||
- test-b / runtime: 1.0
|
||||
Test Output:
|
||||
mutation b test b result output
|
||||
mutation b test result output
|
||||
-----------------------
|
||||
Mutant configuration:
|
||||
Matcher: #<Mutant::Matcher::Config match_expressions=[] subject_ignores=[] subject_selects=[]>
|
||||
|
@ -457,8 +444,8 @@ RSpec.describe Mutant::Reporter::CLI do
|
|||
Kills: 0
|
||||
Alive: 2
|
||||
Runtime: 4.00s
|
||||
Killtime: 4.00s
|
||||
Overhead: 0.00%
|
||||
Killtime: 2.00s
|
||||
Overhead: 100.00%
|
||||
Coverage: 0.00%
|
||||
Expected: 100.00%
|
||||
REPORT
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
RSpec.describe Mutant::Result::Mutation do
|
||||
let(:object) do
|
||||
described_class.new(
|
||||
index: 0,
|
||||
mutation: double('mutation'),
|
||||
test_results: double('test results')
|
||||
)
|
||||
end
|
||||
|
||||
describe '#continue?' do
|
||||
subject { object.continue? }
|
||||
|
||||
it 'forwards calls to mutation' do
|
||||
return_value = double('return value')
|
||||
expect(object.mutation.class).to receive(:continue?).with(object.test_results).and_return(return_value)
|
||||
expect(subject).to be(return_value)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -54,9 +54,8 @@ RSpec.describe Mutant::Runner::Master do
|
|||
end
|
||||
|
||||
context 'stop by fail fast trigger first' do
|
||||
update(:config) { { fail_fast: true } }
|
||||
update(:mutation_b_test_a_result) { { passed: true } }
|
||||
update(:mutation_b_test_b_result) { { passed: true } }
|
||||
update(:config) { { fail_fast: true } }
|
||||
update(:mutation_b_test_result) { { passed: true } }
|
||||
|
||||
before do
|
||||
message_sequence.add(:master, :ready, worker_a)
|
||||
|
@ -82,9 +81,8 @@ RSpec.describe Mutant::Runner::Master do
|
|||
end
|
||||
|
||||
context 'stop by fail fast trigger last' do
|
||||
update(:config) { { fail_fast: true } }
|
||||
update(:mutation_a_test_a_result) { { passed: true } }
|
||||
update(:mutation_a_test_b_result) { { passed: true } }
|
||||
update(:config) { { fail_fast: true } }
|
||||
update(:mutation_a_test_result) { { passed: true } }
|
||||
|
||||
before do
|
||||
message_sequence.add(:master, :ready, worker_a)
|
||||
|
|
|
@ -123,10 +123,9 @@ describe Mutant::Runner::Scheduler do
|
|||
object.job_result(job_a_result)
|
||||
end
|
||||
|
||||
update(:subject_a_result) { { mutation_results: [mutation_a_result] } }
|
||||
update(:mutation_a_test_a_result) { { passed: true } }
|
||||
update(:mutation_a_test_b_result) { { passed: true } }
|
||||
update(:config) { { fail_fast: true } }
|
||||
update(:subject_a_result) { { mutation_results: [mutation_a_result] } }
|
||||
update(:mutation_a_test_result) { { passed: true } }
|
||||
update(:config) { { fail_fast: true } }
|
||||
|
||||
let(:expected_status) do
|
||||
Mutant::Runner::Status.new(
|
||||
|
|
|
@ -23,9 +23,10 @@ RSpec.describe Mutant::Runner::Worker do
|
|||
|
||||
context 'when receving :job command' do
|
||||
|
||||
let(:test_result) { double('Test Result') }
|
||||
|
||||
before do
|
||||
expect(mutation).to receive(:kill).with(config.isolation).and_return(mutation_result).ordered
|
||||
expect(mutation_result).to receive(:update).with(index: job.index).and_return(mutation_result).ordered
|
||||
expect(mutation).to receive(:kill).with(config.isolation, config.integration).and_return(test_result).ordered
|
||||
|
||||
message_sequence.add(:worker, :job, job)
|
||||
message_sequence.add(:parent, :result, job_result)
|
||||
|
@ -37,10 +38,17 @@ RSpec.describe Mutant::Runner::Worker do
|
|||
let(:index) { double('Index') }
|
||||
let(:test_result) { double('Test Result') }
|
||||
let(:mutation) { double('Mutation') }
|
||||
let(:mutation_result) { double('Mutation Result') }
|
||||
let(:job_result) { Mutant::Runner::JobResult.new(job: job, result: mutation_result) }
|
||||
let(:job) { Mutant::Runner::Job.new(index: index, mutation: mutation) }
|
||||
|
||||
let(:mutation_result) do
|
||||
Mutant::Result::Mutation.new(
|
||||
mutation: mutation,
|
||||
index: job.index,
|
||||
test_result: test_result
|
||||
)
|
||||
end
|
||||
|
||||
it 'signals ready and status to parent' do
|
||||
subject
|
||||
end
|
||||
|
|
|
@ -1,53 +0,0 @@
|
|||
RSpec.describe Mutant::Test do
|
||||
let(:object) { described_class.new(integration, expression) }
|
||||
|
||||
let(:integration) { double('Integration', name: 'test-integration') }
|
||||
let(:expression) { double('Expression', syntax: 'test-syntax') }
|
||||
let(:report) { double('Report') }
|
||||
let(:updated_report) { double('Updated Report') }
|
||||
|
||||
describe '#identification' do
|
||||
subject { object.identification }
|
||||
|
||||
it { should eql('test-integration:test-syntax') }
|
||||
end
|
||||
|
||||
describe '#kill' do
|
||||
let(:isolation) { Mutant::Isolation::None }
|
||||
let(:mutation) { double('Mutation') }
|
||||
|
||||
subject { object.kill(isolation, mutation) }
|
||||
|
||||
before do
|
||||
expect(mutation).to receive(:insert)
|
||||
end
|
||||
|
||||
context 'when isolation does not raise' do
|
||||
before do
|
||||
expect(report).to receive(:update).with(test: object).and_return(updated_report)
|
||||
end
|
||||
|
||||
it 'runs test via integration' do
|
||||
expect(integration).to receive(:run).with(object).and_return(report)
|
||||
expect(subject).to be(updated_report)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when isolation does raise' do
|
||||
before do
|
||||
allow(Time).to receive(:now).and_return(Time.at(0))
|
||||
end
|
||||
|
||||
it 'runs test via integration' do
|
||||
expect(integration).to receive(:run).with(object).and_raise(Mutant::Isolation::Error, 'fake message')
|
||||
expect(subject).to eql(Mutant::Result::Test.new(
|
||||
test: object,
|
||||
mutation: mutation,
|
||||
output: 'fake message',
|
||||
passed: false,
|
||||
runtime: 0.0
|
||||
))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,6 +0,0 @@
|
|||
source 'https://rubygems.org'
|
||||
|
||||
gem 'rspec', '~> 2.14.1'
|
||||
gem 'rspec-core', '~> 2.14.1'
|
||||
gem 'mutant', path: '../'
|
||||
gem 'mutant-rspec', path: '../'
|
Loading…
Reference in a new issue