Skip to content

Commit

Permalink
Merge pull request mbj#1431 from mbj/add/source-matcher
Browse files Browse the repository at this point in the history
Add source match expression
  • Loading branch information
mbj committed Mar 24, 2024
2 parents 725181c + 704d6df commit 0d1c70a
Show file tree
Hide file tree
Showing 18 changed files with 295 additions and 22 deletions.
1 change: 1 addition & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ matcher:
- Your::App::Namespace#some_method # select a specific instance method
- Your::App::Namespace.some_method # select a specific class method
- descendants:ApplicationController # select all descendands of application controller (and itself)
- source:lib/**/*.rb # select all subjects that are defined in toplevel constants (modules and classes), recursively
# Expressions of subjects to ignore during mutation testing.
# Multiple entries are allowed and matches from each expression
# are unioned.
Expand Down
4 changes: 3 additions & 1 deletion lib/mutant.rb
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@ module Mutant
require 'mutant/expression/methods'
require 'mutant/expression/namespace'
require 'mutant/expression/parser'
require 'mutant/expression/source'
require 'mutant/test'
require 'mutant/test/runner'
require 'mutant/test/runner/sink'
Expand Down Expand Up @@ -347,7 +348,8 @@ class Config
Expression::Method,
Expression::Methods,
Expression::Namespace::Exact,
Expression::Namespace::Recursive
Expression::Namespace::Recursive,
Expression::Source
]
),
environment_variables: EMPTY_HASH,
Expand Down
2 changes: 1 addition & 1 deletion lib/mutant/bootstrap.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ def self.call(env)
.with(matchable_scopes: matchable_scopes(env))

matched_subjects = env.record(:subject_match) do
Matcher.from_config(env.config.matcher).call(env)
Matcher.expand(env: env).call(env)
end

selected_subjects = subject_select(env, matched_subjects)
Expand Down
3 changes: 2 additions & 1 deletion lib/mutant/expression/descendants.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ def syntax
"descendants:#{const_name}"
end

def matcher
# rubocop:disable Lint/UnusedMethodArgument
def matcher(env:)
Matcher::Descendants.new(const_name: const_name)
end
end # Descendants
Expand Down
4 changes: 3 additions & 1 deletion lib/mutant/expression/method.rb
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,9 @@ def initialize(*)
# Matcher for expression
#
# @return [Matcher]
def matcher
#
# rubocop:disable Lint/UnusedMethodArgument
def matcher(env:)
matcher_candidates = MATCHERS.fetch(scope_symbol)
.map { |submatcher| submatcher.new(scope: scope) }

Expand Down
4 changes: 3 additions & 1 deletion lib/mutant/expression/methods.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@ def initialize(*)
# Matcher on expression
#
# @return [Matcher::Method]
def matcher
#
# rubocop:disable Lint/UnusedMethodArgument
def matcher(env:)
matcher_candidates = MATCHERS.fetch(scope_symbol)
.map { |submatcher| submatcher.new(scope: scope) }

Expand Down
6 changes: 4 additions & 2 deletions lib/mutant/expression/namespace.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@ def initialize(*)
# Matcher for expression
#
# @return [Matcher]
def matcher
#
# rubocop:disable Lint/UnusedMethodArgument
def matcher(env:)
Matcher::Namespace.new(expression: self)
end

Expand Down Expand Up @@ -65,7 +67,7 @@ class Exact < self
# Matcher matcher on expression
#
# @return [Matcher]
def matcher
def matcher(env:)
raw_scope = find_raw_scope

if raw_scope
Expand Down
46 changes: 46 additions & 0 deletions lib/mutant/expression/source.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# frozen_string_literal: true

module Mutant
class Expression
class Source < self
include Anima.new(:glob_expression)

REGEXP = /\Asource:(?<glob_expression>.+)\z/

def syntax
"source:#{glob_expression}"
end

def matcher(env:)
Matcher::Chain.new(matchers: find_matchers(env: env))
end

private

def find_matchers(env:)
scope_names(env: env).uniq.map do |scope_name|
Namespace::Recursive.new(scope_name: scope_name).matcher(env: nil)
end
end

def scope_names(env:)
env.world.pathname.glob(glob_expression).flat_map do |path|
toplevel_consts(env.parser.call(path).node).map(&Unparser.public_method(:unparse))
end
end

def toplevel_consts(node)
children = node.children

case node.type
when :class, :module
[children.fetch(0)]
when :begin
children.flat_map(&method(__method__))
else
EMPTY_ARRAY
end
end
end # Source
end # Expression
end # Mutant
10 changes: 6 additions & 4 deletions lib/mutant/matcher.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,15 @@ class Matcher

# Turn config into matcher
#
# @param [Config] config
# @param [Env] env
#
# @return [Matcher]
def self.from_config(config)
def self.expand(env:)
matcher_config = env.config.matcher

Filter.new(
matcher: Chain.new(matchers: config.subjects.map(&:matcher)),
predicate: method(:allowed_subject?).curry.call(config)
matcher: Chain.new(matchers: matcher_config.subjects.map { |subject| subject.matcher(env: env) }),
predicate: method(:allowed_subject?).curry.call(matcher_config)
)
end

Expand Down
2 changes: 1 addition & 1 deletion lib/mutant/parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ def initialize
#
# @return [AST::Node]
def call(path)
@cache[path] ||= parse(path.read)
@cache[path.expand_path] ||= parse(path.read)
end

private
Expand Down
2 changes: 1 addition & 1 deletion spec/unit/mutant/expression/descendants_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

describe '#matcher' do
def apply
object.matcher
object.matcher(env: instance_double(Mutant::Env))
end

it 'returns expected matcher' do
Expand Down
2 changes: 1 addition & 1 deletion spec/unit/mutant/expression/method_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
end

describe '#matcher' do
subject { object.matcher }
subject { object.matcher(env: instance_double(Mutant::Env)) }

context 'with an instance method' do
let(:input) { instance_method }
Expand Down
2 changes: 1 addition & 1 deletion spec/unit/mutant/expression/methods_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
end

describe '#matcher' do
subject { object.matcher }
subject { object.matcher(env: instance_double(Mutant::Env)) }

let(:scope) do
Mutant::Scope.new(
Expand Down
2 changes: 1 addition & 1 deletion spec/unit/mutant/expression/namespace/exact_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
let(:input) { 'TestApp::Literal' }

describe '#matcher' do
subject { object.matcher }
subject { object.matcher(env: instance_double(Mutant::Env)) }

let(:scope) do
Mutant::Scope.new(
Expand Down
2 changes: 1 addition & 1 deletion spec/unit/mutant/expression/namespace/recursive_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
let(:input) { 'TestApp::Literal*' }

describe '#matcher' do
subject { object.matcher }
subject { object.matcher(env: instance_double(Mutant::Env)) }

it { should eql(Mutant::Matcher::Namespace.new(expression: object)) }
end
Expand Down
Loading

0 comments on commit 0d1c70a

Please sign in to comment.