lib/mutant/integration.rb
# frozen_string_literal: true module Mutant # Abstract base class mutant test framework integrations class Integration include AbstractType, Adamantium, Anima.new( :arguments, :expression_parser, :world ) LOAD_MESSAGE = <<~'MESSAGE' Unable to load integration mutant-%<integration_name>s: %<exception>s You may have to install the gem mutant-%<integration_name>s! MESSAGE CONST_MESSAGE = <<~'MESSAGE' Unable to load integration mutant-%<integration_name>s: %<exception>s This is a bug in the integration you have to report. The integration is supposed to define %<constant_name>s! MESSAGE INTEGRATION_MISSING = <<~'MESSAGE' No test framework integration configured. See https://github.com/mbj/mutant/blob/master/docs/configuration.md#integration MESSAGE private_constant(*constants(false)) class Config include Adamantium, Anima.new(:name, :arguments) DEFAULT = new(arguments: EMPTY_ARRAY, name: nil) TRANSFORM = Transform::Sequence.new( steps: [ Transform::Primitive.new(primitive: Hash), Transform::Hash.new( optional: [ Transform::Hash::Key.new(transform: Transform::STRING, value: 'name'), Transform::Hash::Key.new(transform: Transform::STRING_ARRAY, value: 'arguments') ], required: [] ), Transform::Hash::Symbolize.new, Transform::Success.new(block: DEFAULT.method(:with)) ] ) def merge(other) self.class.new( name: other.name || name, arguments: arguments + other.arguments ) end end # Config # Setup integration # # @param env [Bootstrap] # # @return [Either<String, Integration>] def self.setup(env) integration_config = env.config.integration return Either::Left.new(INTEGRATION_MISSING) unless integration_config.name attempt_require(env).bind { attempt_const_get(env) }.fmap do |klass| klass.new( arguments: integration_config.arguments, expression_parser: env.config.expression_parser, world: env.world ).setup end end # rubocop:disable Style/MultilineBlockChain def self.attempt_require(env) integration_name = env.config.integration.name Either.wrap_error(LoadError) do env.world.kernel.require("mutant/integration/#{integration_name}") end.lmap do |exception| LOAD_MESSAGE % { exception: exception.inspect, integration_name: } end end private_class_method :attempt_require # rubocop:enable Style/MultilineBlockChain def self.attempt_const_get(env) integration_name = env.config.integration.name constant_name = integration_name.capitalize Either.wrap_error(NameError) { const_get(constant_name) }.lmap do |exception| CONST_MESSAGE % { constant_name: "#{self}::#{constant_name}", exception: exception.inspect, integration_name: } end end private_class_method :attempt_const_get # Perform integration setup # # @return [self] def setup self end # Run a collection of tests # # @param [Enumerable<Test>] tests # # @return [Result::Test] abstract_method :call # All tests this integration can run # # Some tests may not be available for mutation testing. # See #available_tests # # @return [Enumerable<Test>] abstract_method :all_tests # All tests available for mutation testing # # Subset ofr #al_tests # # @return [Enumerable<Test>] abstract_method :available_tests private def timer world.timer end end # Integration end # Mutant