Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add source match expression #1431

Merged
merged 1 commit into from
Mar 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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