Add support for --fail-fast flag
* Some long planned runner design changes. * It now also uses the emitter pattern
This commit is contained in:
		
							parent
							
								
									e8ecdab025
								
							
						
					
					
						commit
						8a6a51fd08
					
				
					 16 changed files with 279 additions and 215 deletions
				
			
		| 
						 | 
				
			
			@ -56,12 +56,13 @@ module Mutant
 | 
			
		|||
    #
 | 
			
		||||
    def config
 | 
			
		||||
      Config.new(
 | 
			
		||||
        :cache    => @cache,
 | 
			
		||||
        :debug    => debug?,
 | 
			
		||||
        :matcher  => matcher,
 | 
			
		||||
        :filter   => filter,
 | 
			
		||||
        :strategy => strategy,
 | 
			
		||||
        :reporter => reporter
 | 
			
		||||
        :cache     => @cache,
 | 
			
		||||
        :debug     => debug?,
 | 
			
		||||
        :matcher   => matcher,
 | 
			
		||||
        :filter    => filter,
 | 
			
		||||
        :fail_fast => !!@fail_fast,
 | 
			
		||||
        :strategy  => strategy,
 | 
			
		||||
        :reporter  => reporter
 | 
			
		||||
      )
 | 
			
		||||
    end
 | 
			
		||||
    memoize :config
 | 
			
		||||
| 
						 | 
				
			
			@ -151,6 +152,16 @@ module Mutant
 | 
			
		|||
      @filters << klass.new(filter)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    # Set fail fast
 | 
			
		||||
    #
 | 
			
		||||
    # @api private
 | 
			
		||||
    #
 | 
			
		||||
    # @return [undefined]
 | 
			
		||||
    #
 | 
			
		||||
    def set_fail_fast
 | 
			
		||||
      @fail_fast = true
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    # Set debug mode
 | 
			
		||||
    #
 | 
			
		||||
    # @api private
 | 
			
		||||
| 
						 | 
				
			
			@ -252,6 +263,8 @@ module Mutant
 | 
			
		|||
 | 
			
		||||
      opts.on('--code FILTER', 'Adds a code filter') do |filter|
 | 
			
		||||
        add_filter Mutation::Filter::Code, filter
 | 
			
		||||
      end.on('--fail-fast', 'Fail fast') do
 | 
			
		||||
        set_fail_fast
 | 
			
		||||
      end.on('-d','--debug', 'Enable debugging output') do
 | 
			
		||||
        set_debug
 | 
			
		||||
      end.on_tail('-h', '--help', 'Show this message') do
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,7 +2,7 @@ module Mutant
 | 
			
		|||
  # The configuration of a mutator run
 | 
			
		||||
  class Config
 | 
			
		||||
    include Adamantium::Flat, Anima.new(
 | 
			
		||||
      :cache, :debug, :strategy, :matcher, :filter, :reporter
 | 
			
		||||
      :cache, :debug, :strategy, :matcher, :filter, :reporter, :fail_fast
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    # Enumerate subjects
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,8 +1,57 @@
 | 
			
		|||
module Mutant
 | 
			
		||||
  # Runner baseclass
 | 
			
		||||
  class Runner
 | 
			
		||||
    include Adamantium::Flat, AbstractType, Equalizer.new(:config)
 | 
			
		||||
    extend MethodObject
 | 
			
		||||
    include Adamantium::Flat, AbstractType
 | 
			
		||||
 | 
			
		||||
    REGISTRY = {}
 | 
			
		||||
 | 
			
		||||
    # Register handler
 | 
			
		||||
    #
 | 
			
		||||
    # @param [Class] klass
 | 
			
		||||
    #
 | 
			
		||||
    # @return [undefined]
 | 
			
		||||
    #
 | 
			
		||||
    # @api private
 | 
			
		||||
    #
 | 
			
		||||
    def self.register(klass)
 | 
			
		||||
      REGISTRY[klass]=self
 | 
			
		||||
    end
 | 
			
		||||
    private_class_method :register
 | 
			
		||||
 | 
			
		||||
    # Lookup runner
 | 
			
		||||
    #
 | 
			
		||||
    # @param [Class] klass
 | 
			
		||||
    #
 | 
			
		||||
    # @return [undefined]
 | 
			
		||||
    #
 | 
			
		||||
    # @api private
 | 
			
		||||
    #
 | 
			
		||||
    def self.lookup(klass)
 | 
			
		||||
      current = klass
 | 
			
		||||
      while current
 | 
			
		||||
        handler = REGISTRY.fetch(current) do
 | 
			
		||||
          current = current.superclass; nil
 | 
			
		||||
        end
 | 
			
		||||
        return handler if handler
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      raise ArgumentError, "No handler for: #{klass}"
 | 
			
		||||
    end
 | 
			
		||||
    private_class_method :lookup
 | 
			
		||||
 | 
			
		||||
    # Run runner for object
 | 
			
		||||
    #
 | 
			
		||||
    # @param [Config] config
 | 
			
		||||
    # @param [Object] object
 | 
			
		||||
    #
 | 
			
		||||
    # @return [Runner]
 | 
			
		||||
    #
 | 
			
		||||
    # @api private
 | 
			
		||||
    #
 | 
			
		||||
    def self.run(config, object)
 | 
			
		||||
      handler = lookup(object.class)
 | 
			
		||||
      handler.new(config, object)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    # Return config
 | 
			
		||||
    #
 | 
			
		||||
| 
						 | 
				
			
			@ -27,6 +76,18 @@ module Mutant
 | 
			
		|||
      @end = Time.now
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    # Test if runner should stop
 | 
			
		||||
    #
 | 
			
		||||
    # @return [true]
 | 
			
		||||
    #   if runner should stop
 | 
			
		||||
    #
 | 
			
		||||
    # @return [false]
 | 
			
		||||
    #   otherwise
 | 
			
		||||
    #
 | 
			
		||||
    def stop?
 | 
			
		||||
      !!@stop
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    # Return runtime
 | 
			
		||||
    #
 | 
			
		||||
    # @return [Float]
 | 
			
		||||
| 
						 | 
				
			
			@ -34,22 +95,17 @@ module Mutant
 | 
			
		|||
    # @api private
 | 
			
		||||
    #
 | 
			
		||||
    def runtime
 | 
			
		||||
      @end - @start
 | 
			
		||||
      (@end || Time.now) - @start
 | 
			
		||||
    end
 | 
			
		||||
    memoize :runtime
 | 
			
		||||
 | 
			
		||||
    # Test if runner failed
 | 
			
		||||
    # Return reporter
 | 
			
		||||
    #
 | 
			
		||||
    # @return [true]
 | 
			
		||||
    #   if failed
 | 
			
		||||
    #
 | 
			
		||||
    # @return [false]
 | 
			
		||||
    #   otherwise
 | 
			
		||||
    # @return [Reporter]
 | 
			
		||||
    #
 | 
			
		||||
    # @api private
 | 
			
		||||
    #
 | 
			
		||||
    def failed?
 | 
			
		||||
      !success?
 | 
			
		||||
    def reporter
 | 
			
		||||
      config.reporter
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    # Test if runner is successful
 | 
			
		||||
| 
						 | 
				
			
			@ -64,16 +120,6 @@ module Mutant
 | 
			
		|||
    #
 | 
			
		||||
    abstract_method :success?
 | 
			
		||||
 | 
			
		||||
    # Return reporter
 | 
			
		||||
    #
 | 
			
		||||
    # @return [Reporter]
 | 
			
		||||
    #
 | 
			
		||||
    # @api private
 | 
			
		||||
    #
 | 
			
		||||
    def reporter
 | 
			
		||||
      config.reporter
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
  private
 | 
			
		||||
 | 
			
		||||
    # Perform operation
 | 
			
		||||
| 
						 | 
				
			
			@ -96,5 +142,36 @@ module Mutant
 | 
			
		|||
      reporter.report(object)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    # Perform dispatch
 | 
			
		||||
    #
 | 
			
		||||
    # @return [Enumerable<Runner>]
 | 
			
		||||
    #
 | 
			
		||||
    # @api private
 | 
			
		||||
    #
 | 
			
		||||
    def dispatch(input)
 | 
			
		||||
      collection = []
 | 
			
		||||
      input.each do |object|
 | 
			
		||||
        runner = visit(object)
 | 
			
		||||
        collection << runner
 | 
			
		||||
        if runner.stop?
 | 
			
		||||
          @stop = true
 | 
			
		||||
          break
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
      collection
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    # Visit object
 | 
			
		||||
    #
 | 
			
		||||
    # @param [Object] object
 | 
			
		||||
    #
 | 
			
		||||
    # @return [undefined]
 | 
			
		||||
    #
 | 
			
		||||
    # @api private
 | 
			
		||||
    #
 | 
			
		||||
    def visit(object)
 | 
			
		||||
      Runner.run(config, object)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
  end # Runner
 | 
			
		||||
end # Mutant
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,6 +3,22 @@ module Mutant
 | 
			
		|||
    # Runner for object config
 | 
			
		||||
    class Config < self
 | 
			
		||||
 | 
			
		||||
      register Mutant::Config
 | 
			
		||||
 | 
			
		||||
      # Run runner for object
 | 
			
		||||
      #
 | 
			
		||||
      # @param [Config] config
 | 
			
		||||
      # @param [Object] object
 | 
			
		||||
      #
 | 
			
		||||
      # @return [Runner]
 | 
			
		||||
      #
 | 
			
		||||
      # @api private
 | 
			
		||||
      #
 | 
			
		||||
      def self.run(config)
 | 
			
		||||
        handler = lookup(config.class)
 | 
			
		||||
        handler.new(config)
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      # Return subject runners
 | 
			
		||||
      #
 | 
			
		||||
      # @return [Enumerable<Runner::Subject>]
 | 
			
		||||
| 
						 | 
				
			
			@ -18,7 +34,7 @@ module Mutant
 | 
			
		|||
      # @api private
 | 
			
		||||
      #
 | 
			
		||||
      def failed_subjects
 | 
			
		||||
        subjects.select(&:failed?)
 | 
			
		||||
        subjects.reject(&:success?)
 | 
			
		||||
      end
 | 
			
		||||
      memoize :failed_subjects
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -58,9 +74,7 @@ module Mutant
 | 
			
		|||
      def run_subjects
 | 
			
		||||
        strategy = self.strategy
 | 
			
		||||
        strategy.setup
 | 
			
		||||
        @subjects = config.subjects.map do |subject|
 | 
			
		||||
          Subject.run(config, subject)
 | 
			
		||||
        end
 | 
			
		||||
        @subjects = dispatch(config.subjects)
 | 
			
		||||
        strategy.teardown
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,6 +4,8 @@ module Mutant
 | 
			
		|||
    class Mutation < self
 | 
			
		||||
      include Concord::Public.new(:config, :mutation)
 | 
			
		||||
 | 
			
		||||
      register Mutant::Mutation
 | 
			
		||||
 | 
			
		||||
      # Return killer instance
 | 
			
		||||
      #
 | 
			
		||||
      # @return [Killer]
 | 
			
		||||
| 
						 | 
				
			
			@ -50,7 +52,8 @@ module Mutant
 | 
			
		|||
      #
 | 
			
		||||
      def run
 | 
			
		||||
        @killer = config.strategy.kill(mutation)
 | 
			
		||||
        report(@killer)
 | 
			
		||||
        report(killer)
 | 
			
		||||
        @stop = config.fail_fast && !killer.success?
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
    end # Mutation
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,13 +4,7 @@ module Mutant
 | 
			
		|||
    class Subject < self
 | 
			
		||||
      include Concord::Public.new(:config, :subject)
 | 
			
		||||
 | 
			
		||||
      # Return subject
 | 
			
		||||
      #
 | 
			
		||||
      # @return [Subject]
 | 
			
		||||
      #
 | 
			
		||||
      # @api private
 | 
			
		||||
      #
 | 
			
		||||
      attr_reader :subject
 | 
			
		||||
      register Mutant::Subject
 | 
			
		||||
 | 
			
		||||
      # Initialize object
 | 
			
		||||
      #
 | 
			
		||||
| 
						 | 
				
			
			@ -41,7 +35,7 @@ module Mutant
 | 
			
		|||
      # @api private
 | 
			
		||||
      #
 | 
			
		||||
      def failed_mutations
 | 
			
		||||
        mutations.select(&:failed?)
 | 
			
		||||
        mutations.reject(&:success?)
 | 
			
		||||
      end
 | 
			
		||||
      memoize :failed_mutations
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -70,9 +64,7 @@ module Mutant
 | 
			
		|||
      def run
 | 
			
		||||
        subject = self.subject
 | 
			
		||||
        report(subject)
 | 
			
		||||
        @mutations = subject.map do |mutation|
 | 
			
		||||
          Mutation.run(config, mutation)
 | 
			
		||||
        end
 | 
			
		||||
        @mutations = dispatch(subject.mutations)
 | 
			
		||||
        report(self)
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,38 +3,18 @@ module Mutant
 | 
			
		|||
  class Subject
 | 
			
		||||
    include AbstractType, Adamantium::Flat, Enumerable, Concord::Public.new(:context, :node)
 | 
			
		||||
 | 
			
		||||
    # Enumerate possible mutations
 | 
			
		||||
    # Return mutations
 | 
			
		||||
    #
 | 
			
		||||
    # @return [self]
 | 
			
		||||
    #   returns self if block given
 | 
			
		||||
    #
 | 
			
		||||
    # @return [Enumerator<Mutation>]
 | 
			
		||||
    #   returns eumerator if no block given
 | 
			
		||||
    # @return [Enumerable<Mutation>]
 | 
			
		||||
    #
 | 
			
		||||
    # @api private
 | 
			
		||||
    #
 | 
			
		||||
    def each
 | 
			
		||||
      return to_enum unless block_given?
 | 
			
		||||
 | 
			
		||||
      yield noop_mutation
 | 
			
		||||
 | 
			
		||||
      mutations.each do |mutation|
 | 
			
		||||
        yield mutation
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      self
 | 
			
		||||
    def mutations
 | 
			
		||||
      mutations = []
 | 
			
		||||
      generate_mutations(mutations)
 | 
			
		||||
      mutations
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    # Return noop mutation
 | 
			
		||||
    #
 | 
			
		||||
    # @return [Mutation::Noop]
 | 
			
		||||
    #
 | 
			
		||||
    # @api private
 | 
			
		||||
    #
 | 
			
		||||
    def noop
 | 
			
		||||
      Mutation::Neutral.new(self, node)
 | 
			
		||||
    end
 | 
			
		||||
    memoize :noop
 | 
			
		||||
    memoize :mutations
 | 
			
		||||
 | 
			
		||||
    # Return source path
 | 
			
		||||
    #
 | 
			
		||||
| 
						 | 
				
			
			@ -122,5 +102,15 @@ module Mutant
 | 
			
		|||
      Mutation::Neutral::Noop.new(self, node)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    # Generate mutations
 | 
			
		||||
    #
 | 
			
		||||
    # @param [#<<] emitter
 | 
			
		||||
    #
 | 
			
		||||
    # @return [undefined]
 | 
			
		||||
    #
 | 
			
		||||
    # @api private
 | 
			
		||||
    #
 | 
			
		||||
    abstract_method :generate_mutations
 | 
			
		||||
 | 
			
		||||
  end # Subject
 | 
			
		||||
end # Mutant
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -29,13 +29,16 @@ module Mutant
 | 
			
		|||
 | 
			
		||||
      # Return mutations
 | 
			
		||||
      #
 | 
			
		||||
      # @return [Enumerable<Mutation>]
 | 
			
		||||
      # @param [#<<] emitter
 | 
			
		||||
      #
 | 
			
		||||
      # @return [undefined]
 | 
			
		||||
      #
 | 
			
		||||
      # @api private
 | 
			
		||||
      #
 | 
			
		||||
      def mutations
 | 
			
		||||
        Mutator.each(node).map do |mutant|
 | 
			
		||||
          Mutation::Evil.new(self, mutant)
 | 
			
		||||
      def generate_mutations(emitter)
 | 
			
		||||
        emitter << noop_mutation
 | 
			
		||||
        Mutator.each(node) do |mutant|
 | 
			
		||||
          emitter << Mutation::Evil.new(self, mutant)
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -24,4 +24,7 @@ RSpec.configure do |config|
 | 
			
		|||
  config.include(CompressHelper)
 | 
			
		||||
  config.include(ParserHelper)
 | 
			
		||||
  config.include(Mutant::NodeHelpers)
 | 
			
		||||
  config.mock_with :rspec do |config|
 | 
			
		||||
    config.syntax = [:expect, :should]
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -8,31 +8,41 @@ describe Mutant::Runner::Config, '#subjects' do
 | 
			
		|||
  let(:config) do
 | 
			
		||||
    double(
 | 
			
		||||
      'Config',
 | 
			
		||||
      :subjects => [mutation_subject],
 | 
			
		||||
      :class    => Mutant::Config,
 | 
			
		||||
      :subjects => [subject_a, subject_b],
 | 
			
		||||
      :strategy => strategy,
 | 
			
		||||
      :reporter => reporter
 | 
			
		||||
    )
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  let(:reporter)         { double('Reporter')         }
 | 
			
		||||
  let(:strategy)         { double('Strategy')         }
 | 
			
		||||
  let(:mutation_subject) { double('Mutation subject') }
 | 
			
		||||
  let(:subject_runner)   { double('Subject runner')   }
 | 
			
		||||
 | 
			
		||||
  class DummySubjectRunner
 | 
			
		||||
    include Concord::Public.new(:config, :mutation)
 | 
			
		||||
 | 
			
		||||
    def self.run(*args); new(*args); end
 | 
			
		||||
  end
 | 
			
		||||
  let(:reporter)  { double('Reporter')                   }
 | 
			
		||||
  let(:strategy)  { double('Strategy')                   }
 | 
			
		||||
  let(:subject_a) { double('Subject A')                  }
 | 
			
		||||
  let(:subject_b) { double('Subject B')                  }
 | 
			
		||||
  let(:runner_a)  { double('Runner A', :stop? => stop_a) }
 | 
			
		||||
  let(:runner_b)  { double('Runner B', :stop? => stop_b) }
 | 
			
		||||
 | 
			
		||||
  before do
 | 
			
		||||
    strategy.stub(:setup)
 | 
			
		||||
    strategy.stub(:teardown)
 | 
			
		||||
    reporter.stub(:report => reporter)
 | 
			
		||||
    stub_const('Mutant::Runner::Subject', DummySubjectRunner)
 | 
			
		||||
    Mutant::Runner.stub(:run).with(config, subject_a).and_return(runner_a)
 | 
			
		||||
    Mutant::Runner.stub(:run).with(config, subject_b).and_return(runner_b)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  it { should eql([DummySubjectRunner.new(config, mutation_subject)]) }
 | 
			
		||||
  context 'without earily stop' do
 | 
			
		||||
    let(:stop_a) { false }
 | 
			
		||||
    let(:stop_b) { false }
 | 
			
		||||
    it { should eql([runner_a, runner_b]) }
 | 
			
		||||
    it_should_behave_like 'an idempotent method'
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  context 'with earily stop' do
 | 
			
		||||
    let(:stop_a) { true  }
 | 
			
		||||
    let(:stop_b) { false }
 | 
			
		||||
    it { should eql([runner_a]) }
 | 
			
		||||
    it_should_behave_like 'an idempotent method'
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  it_should_behave_like 'an idempotent method'
 | 
			
		||||
end
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,50 +3,45 @@ require 'spec_helper'
 | 
			
		|||
describe Mutant::Runner::Config, '#success?' do
 | 
			
		||||
  subject { object.success? }
 | 
			
		||||
 | 
			
		||||
  let(:object) { described_class.run(config) }
 | 
			
		||||
  let(:object) { described_class.new(config) }
 | 
			
		||||
 | 
			
		||||
  let(:config) do
 | 
			
		||||
    double(
 | 
			
		||||
      'Config',
 | 
			
		||||
      :reporter => reporter,
 | 
			
		||||
      :strategy => strategy,
 | 
			
		||||
      :subjects => subjects
 | 
			
		||||
      :subjects => [subject_a, subject_b]
 | 
			
		||||
    )
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  let(:reporter)  { double('Reporter')                    }
 | 
			
		||||
  let(:strategy)  { double('Strategy')                    }
 | 
			
		||||
  let(:subjects)  { [subject_a, subject_b]                }
 | 
			
		||||
  let(:subject_a) { double('Subject A', :fails? => false) }
 | 
			
		||||
  let(:subject_b) { double('Subject B', :fails? => false) }
 | 
			
		||||
 | 
			
		||||
  class DummySubjectRunner
 | 
			
		||||
    include Concord::Public.new(:config, :subject)
 | 
			
		||||
 | 
			
		||||
    def self.run(*args)
 | 
			
		||||
      new(*args)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def failed?
 | 
			
		||||
      @subject.fails?
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
  let(:reporter)  { double('Reporter')                                           }
 | 
			
		||||
  let(:strategy)  { double('Strategy')                                           }
 | 
			
		||||
  let(:subject_a) { double('Subject A')                                          }
 | 
			
		||||
  let(:subject_b) { double('Subject B')                                          }
 | 
			
		||||
  let(:runner_a)  { double('Runner A', :stop? => stop_a, :success? => success_a) }
 | 
			
		||||
  let(:runner_b)  { double('Runner B', :stop? => stop_b, :success? => success_b) }
 | 
			
		||||
 | 
			
		||||
  before do
 | 
			
		||||
    stub_const('Mutant::Runner::Subject', DummySubjectRunner)
 | 
			
		||||
    reporter.stub(:report => reporter)
 | 
			
		||||
    strategy.stub(:setup)
 | 
			
		||||
    strategy.stub(:teardown)
 | 
			
		||||
    Mutant::Runner.stub(:run).with(config, subject_a).and_return(runner_a)
 | 
			
		||||
    Mutant::Runner.stub(:run).with(config, subject_b).and_return(runner_b)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  context 'without failed subjects' do
 | 
			
		||||
    let(:stop_a)    { false }
 | 
			
		||||
    let(:stop_b)    { false }
 | 
			
		||||
    let(:success_a) { true  }
 | 
			
		||||
    let(:success_b) { true  }
 | 
			
		||||
    it { should be(true) }
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  context 'with failing subjects' do
 | 
			
		||||
    before do
 | 
			
		||||
      subject_a.stub(:fails? => true)
 | 
			
		||||
    end
 | 
			
		||||
    let(:stop_a)    { false }
 | 
			
		||||
    let(:stop_b)    { false }
 | 
			
		||||
    let(:success_a) { false }
 | 
			
		||||
    let(:success_b) { true  }
 | 
			
		||||
 | 
			
		||||
    it { should be(false) }
 | 
			
		||||
  end
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,33 +0,0 @@
 | 
			
		|||
require 'spec_helper'
 | 
			
		||||
 | 
			
		||||
describe Mutant::Runner, '#failed?' do
 | 
			
		||||
  subject { object.failed? }
 | 
			
		||||
 | 
			
		||||
  let(:object) { class_under_test.run(config) }
 | 
			
		||||
 | 
			
		||||
  let(:config) { double('Config') }
 | 
			
		||||
  let(:class_under_test) do
 | 
			
		||||
    success = self.success
 | 
			
		||||
 | 
			
		||||
    Class.new(described_class) do
 | 
			
		||||
      define_method :success? do
 | 
			
		||||
        success
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      define_method :run do
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  context 'when runner is successful' do
 | 
			
		||||
    let(:success) { true }
 | 
			
		||||
 | 
			
		||||
    it { should be(false) }
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  context 'when runner is NOT successful' do
 | 
			
		||||
    let(:success) { false }
 | 
			
		||||
 | 
			
		||||
    it { should be(true) }
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
| 
						 | 
				
			
			@ -6,15 +6,18 @@ describe Mutant::Runner::Mutation, '#killer' do
 | 
			
		|||
  let(:config) do
 | 
			
		||||
    double(
 | 
			
		||||
      'Config',
 | 
			
		||||
      :reporter => reporter,
 | 
			
		||||
      :strategy => strategy
 | 
			
		||||
      :fail_fast => fail_fast,
 | 
			
		||||
      :reporter  => reporter,
 | 
			
		||||
      :strategy  => strategy
 | 
			
		||||
    )
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  let(:reporter) { double('Reporter') }
 | 
			
		||||
  let(:mutation) { double('Mutation') }
 | 
			
		||||
  let(:strategy) { double('Strategy') }
 | 
			
		||||
  let(:killer)   { double('Killer')   }
 | 
			
		||||
  let(:reporter)  { double('Reporter')                             }
 | 
			
		||||
  let(:mutation)  { double('Mutation', :class => Mutant::Mutation) }
 | 
			
		||||
  let(:strategy)  { double('Strategy')                             }
 | 
			
		||||
  let(:killer)    { double('Killer', :success? => success)         }
 | 
			
		||||
  let(:fail_fast) { false                                          }
 | 
			
		||||
  let(:success)   { false                                          }
 | 
			
		||||
 | 
			
		||||
  subject { object.killer }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,47 +3,49 @@ require 'spec_helper'
 | 
			
		|||
describe Mutant::Runner::Subject, '#success?' do
 | 
			
		||||
  subject { object.success? }
 | 
			
		||||
 | 
			
		||||
  let(:object) { described_class.run(config, mutation_subject) }
 | 
			
		||||
  let(:object) { described_class.new(config, mutation_subject) }
 | 
			
		||||
 | 
			
		||||
  let(:mutation_subject) {
 | 
			
		||||
    double(
 | 
			
		||||
      'Subject',
 | 
			
		||||
      :class     => Mutant::Subject,
 | 
			
		||||
      :mutations => [mutation_a, mutation_b]
 | 
			
		||||
    )
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  let(:reporter)         { double('Reporter')                      }
 | 
			
		||||
  let(:mutation_subject) { double('Subject', :map => mutations)    }
 | 
			
		||||
  let(:config)           { double('Config', :reporter => reporter) }
 | 
			
		||||
  let(:mutation_a)       { double('Mutation A', :failed? => false) }
 | 
			
		||||
  let(:mutation_b)       { double('Mutation B', :failed? => false) }
 | 
			
		||||
  let(:mutations)        { [mutation_a, mutation_b]                }
 | 
			
		||||
  let(:mutation_a)       { double('Mutation A')                    }
 | 
			
		||||
  let(:mutation_b)       { double('Mutation B')                    }
 | 
			
		||||
 | 
			
		||||
  let(:runner_a)         { double('Runner A', :success? => success_a, :stop? => stop_a) }
 | 
			
		||||
  let(:runner_b)         { double('Runner B', :success? => success_b, :stop? => stop_b) }
 | 
			
		||||
 | 
			
		||||
  before do
 | 
			
		||||
    reporter.stub(:report => reporter)
 | 
			
		||||
    Mutant::Runner.stub(:run).with(config, mutation_a).and_return(runner_a)
 | 
			
		||||
    Mutant::Runner.stub(:run).with(config, mutation_b).and_return(runner_b)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  class DummyMutationRunner
 | 
			
		||||
    include Concord::Public.new(:config, :mutation)
 | 
			
		||||
 | 
			
		||||
    def self.run(*args)
 | 
			
		||||
      new(*args)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def failed?
 | 
			
		||||
      @mutation.failed?
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  before do
 | 
			
		||||
    stub_const('Mutant::Runner::Mutation', DummyMutationRunner)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  context 'without evil failed mutations' do
 | 
			
		||||
    it { should be(true) }
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  context 'with failing noop mutation' do
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  context 'with failing evil mutations' do
 | 
			
		||||
    before do
 | 
			
		||||
      mutation_a.stub(:failed? => true)
 | 
			
		||||
    end
 | 
			
		||||
  context 'with failing mutations' do
 | 
			
		||||
    let(:stop_a)    { false }
 | 
			
		||||
    let(:stop_b)    { false }
 | 
			
		||||
    let(:success_a) { false }
 | 
			
		||||
    let(:success_b) { true  }
 | 
			
		||||
 | 
			
		||||
    it { should be(false) }
 | 
			
		||||
 | 
			
		||||
    it_should_behave_like 'an idempotent method'
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  context 'without failing mutations' do
 | 
			
		||||
    let(:stop_a)    { false }
 | 
			
		||||
    let(:stop_b)    { false }
 | 
			
		||||
    let(:success_a) { true  }
 | 
			
		||||
    let(:success_b) { true  }
 | 
			
		||||
 | 
			
		||||
    it { should be(true) }
 | 
			
		||||
 | 
			
		||||
    it_should_behave_like 'an idempotent method'
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,31 +0,0 @@
 | 
			
		|||
require 'spec_helper'
 | 
			
		||||
 | 
			
		||||
describe Mutant::Subject, '#each' do
 | 
			
		||||
  subject { object.each { |item| yields << item } }
 | 
			
		||||
 | 
			
		||||
  let(:class_under_test) do
 | 
			
		||||
    mutations = [mutation_a, mutation_b]
 | 
			
		||||
    Class.new(described_class) do
 | 
			
		||||
      define_method(:mutations) { mutations }
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  let(:object)     { class_under_test.new(context, node) }
 | 
			
		||||
  let(:yields)     { []                                  }
 | 
			
		||||
  let(:node)       { double('Node')                      }
 | 
			
		||||
  let(:context)    { double('Context')                   }
 | 
			
		||||
  let(:mutant)     { double('Mutant')                    }
 | 
			
		||||
  let(:mutation_a) { double('Mutation A')                }
 | 
			
		||||
  let(:mutation_b) { double('Mutation B')                }
 | 
			
		||||
 | 
			
		||||
  it_should_behave_like 'an #each method'
 | 
			
		||||
 | 
			
		||||
  let(:neutral_mutation) do
 | 
			
		||||
    Mutant::Mutation::Neutral.new(object, node)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  it 'yields mutations' do
 | 
			
		||||
    expect { subject }.to change { yields.dup }.from([])
 | 
			
		||||
      .to([neutral_mutation, mutation_a, mutation_b])
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
							
								
								
									
										23
									
								
								spec/unit/mutant/subject/mutations_spec.rb
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								spec/unit/mutant/subject/mutations_spec.rb
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,23 @@
 | 
			
		|||
require 'spec_helper'
 | 
			
		||||
 | 
			
		||||
describe Mutant::Subject, '#mutations' do
 | 
			
		||||
  subject { object.mutations }
 | 
			
		||||
 | 
			
		||||
  let(:class_under_test) do
 | 
			
		||||
    mutation_a, mutation_b = self.mutation_a, self.mutation_b
 | 
			
		||||
    Class.new(described_class) do
 | 
			
		||||
      define_method(:generate_mutations) do |emitter|
 | 
			
		||||
        emitter << mutation_a
 | 
			
		||||
        emitter << mutation_b
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  let(:object)     { class_under_test.new(context, node) }
 | 
			
		||||
  let(:node)       { double('Node')                      }
 | 
			
		||||
  let(:context)    { double('Context')                   }
 | 
			
		||||
  let(:mutation_a) { double('Mutation A')                }
 | 
			
		||||
  let(:mutation_b) { double('Mutation B')                }
 | 
			
		||||
 | 
			
		||||
  it { should eql([mutation_a, mutation_b]) }
 | 
			
		||||
end
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue