Add mutation Date.parse -> other parse methods

- Adds receiver specific selector mutations
- Closes #432
This commit is contained in:
John Backus 2015-10-08 21:58:59 -07:00
parent 9aec65b847
commit 7931a01476
8 changed files with 158 additions and 2 deletions

View file

@ -1,3 +1,7 @@
# Unreleased
* Add mutation from `Date.parse` to more strict parsing methods #448
# v0.8.5 2015-09-11
* Fix misimplementation of block gluing operator that

View file

@ -1,3 +1,3 @@
---
threshold: 18
total_score: 1233
total_score: 1239

View file

@ -50,6 +50,7 @@ require 'mutant/ast/named_children'
require 'mutant/ast/node_predicates'
require 'mutant/ast/meta'
require 'mutant/ast/meta/send'
require 'mutant/ast/meta/const'
require 'mutant/ast/meta/symbol'
require 'mutant/ast/meta/optarg'
require 'mutant/ast/meta/resbody'

View file

@ -0,0 +1,24 @@
module Mutant
module AST
# Node meta information mixin
module Meta
# Metadata for const nodes
class Const
include NamedChildren, Concord.new(:node), NodePredicates
children :base, :name
# Test if AST node is possibly a top level constant
#
# @return [Boolean]
#
# @api private
def possible_top_level?
base.nil? || n_cbase?(base)
end
end # Const
end # Meta
end # AST
end # Mutant

View file

@ -5,7 +5,7 @@ module Mutant
# Metadata for send nodes
class Send
include NamedChildren, Concord.new(:node)
include NamedChildren, Concord.new(:node), NodePredicates
children :receiver, :selector
@ -56,6 +56,17 @@ module Mutant
Types::BINARY_METHOD_OPERATORS.include?(selector)
end
# Test if receiver is possibly a top level constant
#
# @return [Boolean]
#
# @api private
def receiver_possible_top_level_const?
return false unless receiver && n_const?(receiver)
Const.new(receiver).possible_top_level?
end
end # Send
end # Meta
end # AST

View file

@ -3,6 +3,7 @@ module Mutant
class Node
# Namespace for send mutators
# rubocop:disable ClassLength
class Send < self
include AST::Types
@ -34,6 +35,12 @@ module Mutant
:< => %i[== <= eql? equal?]
)
RECEIVER_SELECTOR_REPLACEMENTS = IceNine.deep_freeze(
Date: {
parse: %i[jd civil strptime iso8601 rfc3339 xmlschema rfc2822 rfc822 httpdate jisx0301]
}
)
private
# Perform dispatch
@ -92,10 +99,25 @@ module Mutant
emit_selector_replacement
emit_const_get_mutation
emit_argument_propagation
emit_receiver_selector_mutations
mutate_receiver
mutate_arguments
end
# Emit selector mutations specific to top level constants
#
# @return [undefined]
#
# @api private
def emit_receiver_selector_mutations
return unless meta.receiver_possible_top_level_const?
RECEIVER_SELECTOR_REPLACEMENTS
.fetch(receiver.children.last, EMPTY_HASH)
.fetch(selector, EMPTY_ARRAY)
.each(&method(:emit_selector))
end
# Emit mutation from `const_get` to const literal
#
# @return [undefined]

57
meta/date.rb Normal file
View file

@ -0,0 +1,57 @@
Mutant::Meta::Example.add do
source 'Date.parse(nil)'
singleton_mutations
mutation 'Date.parse'
mutation 'self.parse(nil)'
mutation 'Date'
mutation 'Date.jd(nil)'
mutation 'Date.civil(nil)'
mutation 'Date.strptime(nil)'
mutation 'Date.iso8601(nil)'
mutation 'Date.rfc3339(nil)'
mutation 'Date.xmlschema(nil)'
mutation 'Date.rfc2822(nil)'
mutation 'Date.rfc822(nil)'
mutation 'Date.httpdate(nil)'
mutation 'Date.jisx0301(nil)'
end
Mutant::Meta::Example.add do
source '::Date.parse(nil)'
singleton_mutations
mutation '::Date.parse'
mutation 'Date.parse(nil)'
mutation 'self.parse(nil)'
mutation '::Date'
mutation '::Date.jd(nil)'
mutation '::Date.civil(nil)'
mutation '::Date.strptime(nil)'
mutation '::Date.iso8601(nil)'
mutation '::Date.rfc3339(nil)'
mutation '::Date.xmlschema(nil)'
mutation '::Date.rfc2822(nil)'
mutation '::Date.rfc822(nil)'
mutation '::Date.httpdate(nil)'
mutation '::Date.jisx0301(nil)'
end
Mutant::Meta::Example.add do
source 'Date.iso8601(nil)'
singleton_mutations
mutation 'Date.iso8601'
mutation 'self.iso8601(nil)'
mutation 'Date'
end
Mutant::Meta::Example.add do
source 'Foo::Date.parse(nil)'
singleton_mutations
mutation 'Foo::Date.parse'
mutation 'Foo::Date'
mutation 'Date.parse(nil)'
mutation 'self.parse(nil)'
end

View file

@ -0,0 +1,37 @@
RSpec.describe Mutant::AST::Meta::Send, '#receiver_possible_top_level_const?' do
subject { described_class.new(node).receiver_possible_top_level_const? }
def parse(source)
Parser::CurrentRuby.parse(source)
end
context 'when implicit top level const' do
let(:node) { parse('Foo.bar') }
it { should be true }
end
context 'when cbase' do
let(:node) { parse('::Foo.bar') }
it { should be true }
end
context 'when nested const' do
let(:node) { parse('Baz::Foo.bar') }
it { should be false }
end
context 'when no receiver' do
let(:node) { parse('bar') }
it { should be false }
end
context 'when send receiver' do
let(:node) { parse('foo.bar') }
it { should be false }
end
end