# frozen_string_literal: true module Mutant # Namespace and mixin module for results module Result # CoverageMetric mixin module CoverageMetric FULL_COVERAGE = Rational(1).freeze private_constant(*constants(false)) # Observed coverage # # @return [Rational] def coverage if amount_mutation_results.zero? FULL_COVERAGE else Rational(amount_mutations_killed, amount_mutation_results) end end end # CoverageMetric # Class level mixin module ClassMethods # Generate a sum method from name and collection # # @param [Symbol] name # the attribute name on collection item and method name to use # # @param [Symbol] collection # the attribute name used to receive collection # # @return [undefined] def sum(name, collection) define_method(name) do public_send(collection).map(&name).reduce(0, :+) end memoize(name) end # Delegate a method to child def delegate(name, target) define_method(name) do public_send(target).public_send(name) end end end # ClassMethods private_constant(*constants(false)) # Mutant overhead running mutatet tests # # This is NOT the overhead of mutation testing, just an engine specific # measurement for the efficiency of the parallelization engine, kill # isolation etc. # # @return [Float] def overhead runtime - killtime end # Hook called when module gets included # # @param [Class, Module] host # # @return [undefined] def self.included(host) host.class_eval do include Adamantium extend ClassMethods end end # Env result object class Env include CoverageMetric, Result, Anima.new( :env, :runtime, :subject_results ) # Test if run is successful # # @return [Boolean] def success? coverage.eql?(Rational(1)) end memoize :success? # Failed subject results # # @return [Array] def failed_subject_results subject_results.reject(&:success?) end sum :amount_mutation_results, :subject_results sum :amount_mutations_alive, :subject_results sum :amount_mutations_killed, :subject_results sum :amount_timeouts, :subject_results sum :killtime, :subject_results # Amount of mutations # # @return [Integer] def amount_mutations env.mutations.length end # Test if processing needs to stop # # @return [Boolean] # def stop? env.config.fail_fast && !subject_results.all?(&:success?) end end # Env # Test result class Test include Anima.new(:passed, :runtime) class VoidValue < self include Singleton # Initialize object # # @return [undefined] def initialize super( passed: false, runtime: 0.0, ) end end # VoidValue end # Test # Subject result class Subject include CoverageMetric, Result, Anima.new( :coverage_results, :subject, :tests ) sum :killtime, :coverage_results sum :runtime, :coverage_results # Test if subject was processed successful # # @return [Boolean] def success? uncovered_results.empty? end # Alive mutations # # @return [Array] def uncovered_results coverage_results.reject(&:success?) end memoize :uncovered_results # Amount of mutations # # @return [Integer] def amount_mutation_results coverage_results.length end # Amount of mutations # # @return [Integer] def amount_timeouts coverage_results.count(&:timeout?) end # Amount of mutations # # @return [Integer] def amount_mutations subject.mutations.length end # Number of killed mutations # # @return [Integer] def amount_mutations_killed covered_results.length end # Number of alive mutations # # @return [Integer] def amount_mutations_alive uncovered_results.length end private def covered_results coverage_results.select(&:success?) end memoize :covered_results end # Subject # Coverage of a mutation against criteria class Coverage include Result, Anima.new( :mutation_result, :criteria_result ) delegate :killtime, :mutation_result delegate :runtime, :mutation_result delegate :success?, :criteria_result delegate :timeout?, :mutation_result end # Coverage class CoverageCriteria include Result, Anima.new(*Config::CoverageCriteria.anima.attribute_names) # Test if one coverage criteria indicates success # # @return [Boolean] def success? process_abort || test_result || timeout end end class MutationIndex include Anima.new( :isolation_result, :mutation_index, :runtime ) end # MutationIndex # Mutation result class Mutation include Result, Anima.new( :isolation_result, :mutation, :runtime ) # Create mutation criteria results # # @praam [Result::CoverageCriteria] def criteria_result(coverage_criteria) CoverageCriteria.new( process_abort: coverage_criteria.process_abort && process_abort?, test_result: coverage_criteria.test_result && test_result_success?, timeout: coverage_criteria.timeout && timeout? ) end # Time the tests had been running # # @return [Float] def killtime isolation_result.value&.runtime || 0.0 end # Test for timeout # # @return [Boolean] def timeout? !isolation_result.timeout.nil? end # Test for unexpected process abort # # @return [Boolean] def process_abort? process_status = isolation_result.process_status or return false !timeout? && !process_status.success? end private # Test if mutation was handled successfully # # @return [Boolean] def test_result_success? isolation_result.valid_value? && mutation.class.success?(isolation_result.value) end memoize :test_result_success? end # Mutation end # Result end # Mutant