Skip to content

Commit

Permalink
Upgrade to unparser 0.7.x interface
Browse files Browse the repository at this point in the history
  • Loading branch information
mbj committed Oct 30, 2024
1 parent a0ff19a commit 2c14a45
Show file tree
Hide file tree
Showing 25 changed files with 218 additions and 109 deletions.
45 changes: 23 additions & 22 deletions Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
PATH
remote: ../unparser
specs:
unparser (0.7.0)
diff-lcs (~> 1.3)
parser (>= 3.3.0)

PATH
remote: .
specs:
Expand All @@ -6,60 +13,53 @@ PATH
parser (~> 3.3.0)
regexp_parser (~> 2.9.0)
sorbet-runtime (~> 0.5.0)
unparser (~> 0.6.14)
unparser (~> 0.7.0)

GEM
remote: https://rubygems.org/
specs:
ast (2.4.2)
diff-lcs (1.5.1)
json (2.7.2)
json (2.7.5)
language_server-protocol (3.17.0.3)
parallel (1.25.1)
parser (3.3.2.0)
parallel (1.26.3)
parser (3.3.5.0)
ast (~> 2.4.1)
racc
racc (1.8.0)
racc (1.8.1)
rainbow (3.1.1)
regexp_parser (2.9.2)
rexml (3.2.9)
strscan
rspec (3.13.0)
rspec-core (~> 3.13.0)
rspec-expectations (~> 3.13.0)
rspec-mocks (~> 3.13.0)
rspec-core (3.13.0)
rspec-core (3.13.2)
rspec-support (~> 3.13.0)
rspec-expectations (3.13.0)
rspec-expectations (3.13.3)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.13.0)
rspec-its (1.3.0)
rspec-its (1.3.1)
rspec-core (>= 3.0.0)
rspec-expectations (>= 3.0.0)
rspec-mocks (3.13.1)
rspec-mocks (3.13.2)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.13.0)
rspec-support (3.13.1)
rubocop (1.64.1)
rubocop (1.67.0)
json (~> 2.3)
language_server-protocol (>= 3.17.0)
parallel (~> 1.10)
parser (>= 3.3.0.2)
rainbow (>= 2.2.2, < 4.0)
regexp_parser (>= 1.8, < 3.0)
rexml (>= 3.2.5, < 4.0)
rubocop-ast (>= 1.31.1, < 2.0)
regexp_parser (>= 2.4, < 3.0)
rubocop-ast (>= 1.32.2, < 2.0)
ruby-progressbar (~> 1.7)
unicode-display_width (>= 2.4.0, < 3.0)
rubocop-ast (1.31.3)
rubocop-ast (1.33.0)
parser (>= 3.3.1.0)
ruby-progressbar (1.13.0)
sorbet-runtime (0.5.11422)
strscan (3.1.0)
unicode-display_width (2.5.0)
unparser (0.6.14)
diff-lcs (~> 1.3)
parser (>= 3.3.0)
sorbet-runtime (0.5.11625)
unicode-display_width (2.6.0)

PLATFORMS
ruby
Expand All @@ -70,6 +70,7 @@ DEPENDENCIES
rspec-core (~> 3.10)
rspec-its (~> 1.3.0)
rubocop (~> 1.7)
unparser!

BUNDLED WITH
2.5.6
1 change: 1 addition & 0 deletions Gemfile.shared
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
gem 'unparser', path: '../unparser'
2 changes: 1 addition & 1 deletion lib/mutant/cli/command/util.rb
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ def print_mutations(target)
node: target.node
).each do |mutation|
Reporter::CLI::Printer::Mutation.call(
object: Mutant::Mutation::Evil.new(subject: target, node: mutation),
object: Mutant::Mutation::Evil.from_node(subject: target, node: mutation).from_right,
output: world.stdout
)
end
Expand Down
4 changes: 2 additions & 2 deletions lib/mutant/meta/example.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class Expected
#
# @return [Verification]
def verification
Verification.new(example: self, mutations: generated)
Verification.from_mutations(example: self, mutations: generated)
end
memoize :verification

Expand Down Expand Up @@ -61,7 +61,7 @@ def generated
config: Mutation::Config::DEFAULT.with(operators:),
node:
).map do |node|
Mutation::Evil.new(subject: self, node:)
Mutation::Evil.from_node(subject: self, node:)
end
end
memoize :generated
Expand Down
46 changes: 30 additions & 16 deletions lib/mutant/meta/example/verification.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,24 @@ module Meta
class Example
# Example verification
class Verification
include Adamantium, Anima.new(:example, :mutations)
include Adamantium, Anima.new(:example, :invalid, :valid)

def self.from_mutations(example:, mutations:)
valid, invalid = [], []

mutations.each do |mutation|
mutation.either(invalid.public_method(:<<), valid.public_method(:<<))
end

new(example:, invalid:, valid:)
end

# Test if mutation was verified successfully
#
# @return [Boolean]
def success?
[
original_verification,
original_verification_report,
invalid,
missing,
no_diffs,
Expand All @@ -29,12 +39,12 @@ def error_report

def reports
reports = [example.location]
reports.concat(original)
reports.concat(original_verification)
reports.concat(original_report)
reports.concat(original_verification_report)
reports.concat(make_report('Missing mutations:', missing))
reports.concat(make_report('Unexpected mutations:', unexpected))
reports.concat(make_report('No-Diff mutations:', no_diffs))
reports.concat(invalid)
reports.concat(invalid_report)
end

def make_report(label, mutations)
Expand All @@ -52,15 +62,15 @@ def report_mutation(mutation)
]
end

def original
def original_report
[
"Original: (operators: #{example.operators.class.operators_name})",
example.node,
example.original_source
]
end

def original_verification
def original_verification_report
validation = Unparser::Validation.from_string(example.original_source)
if validation.success?
[]
Expand All @@ -77,30 +87,34 @@ def prefix(prefix, string)
end.join
end

def invalid
mutations.each_with_object([]) do |mutation, aggregate|
validation = Unparser::Validation.from_node(mutation.node)
aggregate << prefix('[invalid-mutation]', validation.report) unless validation.success?
def invalid_report
invalid.map do |validation|
prefix('[invalid-mutation]', validation.report)
end
end
memoize :invalid
memoize :invalid_report

def unexpected
mutations.reject do |mutation|
valid.reject do |mutation|
example.expected.any? { |expected| expected.node.eql?(mutation.node) }
end
end
memoize :unexpected

def missing
(example.expected.map(&:node) - mutations.map(&:node)).map do |node|
Mutation::Evil.new(subject: example, node:)
example.expected.each_with_object([]) do |expected, aggregate|
next if valid.any? { |mutation| expected.node.eql?(mutation.node) }
aggregate << Mutation::Evil.new(
node: expected.node,
source: expected.original_source,
subject: example
)
end
end
memoize :missing

def no_diffs
mutations.select { |mutation| mutation.source.eql?(example.original_source_generated) }
valid.select { |mutation| mutation.source.eql?(example.original_source_generated) }
end
memoize :no_diffs

Expand Down
18 changes: 9 additions & 9 deletions lib/mutant/mutation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,19 @@ module Mutant
# Represent a mutated node with its subject
class Mutation
include AbstractType, Adamantium
include Anima.new(:subject, :node)
include Anima.new(:subject, :node, :source)

CODE_DELIMITER = "\0"
CODE_RANGE = (..4)

def self.from_node(subject:, node:)
ast = Unparser::AST.from_node(node:)

Unparser
.unparse_validate_ast_either(ast:)
.fmap { |source| new(node:, source:, subject:) }
end

# Mutation identification code
#
# @return [String]
Expand All @@ -17,14 +25,6 @@ def code
end
memoize :code

# Normalized mutation source
#
# @return [String]
def source
Unparser.unparse(node)
end
memoize :source

# Identification string
#
# @return [String]
Expand Down
6 changes: 3 additions & 3 deletions lib/mutant/parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,11 @@ def call(path)
private

def parse(source)
node, comments = Unparser.parse_with_comments(source)
ast = Unparser.parse_ast_either(source).from_right

AST.new(
node:,
comment_associations: ::Parser::Source::Comment.associate_by_identity(node, comments)
comment_associations: ::Parser::Source::Comment.associate_by_identity(ast.node, ast.comments),
node: ast.node
)
end

Expand Down
13 changes: 10 additions & 3 deletions lib/mutant/subject.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,13 @@ def mutations
Mutator::Node.mutate(
config: config.mutation,
node:
).map do |mutant|
Mutation::Evil.new(subject: self, node: wrap_node(mutant))
).each_with_object([]) do |mutant, aggregate|
Mutation::Evil
.from_node(subject: self, node: wrap_node(mutant))
.either(
->(validation) { $stderr.puts("generated invalid mutation: #{validation.report}") },
aggregate.method(:<<)
)
end
)
end
Expand Down Expand Up @@ -92,7 +97,9 @@ def source
private

def neutral_mutation
Mutation::Neutral.new(subject: self, node: wrap_node(node))
Mutation::Neutral
.from_node(subject: self, node: wrap_node(node))
.from_right
end

def wrap_node(node)
Expand Down
2 changes: 1 addition & 1 deletion mutant.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ Gem::Specification.new do |gem|
gem.add_runtime_dependency('parser', '~> 3.3.0')
gem.add_runtime_dependency('regexp_parser', '~> 2.9.0')
gem.add_runtime_dependency('sorbet-runtime', '~> 0.5.0')
gem.add_runtime_dependency('unparser', '~> 0.6.14')
gem.add_runtime_dependency('unparser', '~> 0.7.0')

gem.add_development_dependency('rspec', '~> 3.10')
gem.add_development_dependency('rspec-core', '~> 3.10')
Expand Down
2 changes: 1 addition & 1 deletion scripts/devloop.sh
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
while inotifywait lib/**/*.rb meta/**/*.rb spec/**/*.rb Gemfile Gemfile.shared mutant.gemspec; do
while inotifywait ../unparser/lib/**/*.rb lib/**/*.rb meta/**/*.rb spec/**/*.rb Gemfile Gemfile.shared mutant.gemspec; do
bundle exec mutant environment test run --fail-fast spec/unit \
&& bundle exec mutant run --fail-fast --since main --zombie -- 'Mutant*' \
&& bundle exec rubocop
Expand Down
4 changes: 2 additions & 2 deletions spec/support/shared_context.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,11 @@ def setup_shared_context
let(:subjects) { [subject_a] }

let(:mutation_a) do
Mutant::Mutation::Evil.new(subject: subject_a, node: mutation_a_node)
Mutant::Mutation::Evil.from_node(subject: subject_a, node: mutation_a_node).from_right
end

let(:mutation_b) do
Mutant::Mutation::Evil.new(subject: subject_a, node: mutation_b_node)
Mutant::Mutation::Evil.from_node(subject: subject_a, node: mutation_b_node).from_right
end

let(:job_a) do
Expand Down
20 changes: 15 additions & 5 deletions spec/unit/mutant/meta/example/verification_spec.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# frozen_string_literal: true

RSpec.describe Mutant::Meta::Example::Verification do
let(:object) { described_class.new(example:, mutations:) }
let(:object) { described_class.from_mutations(example:, mutations:) }
let(:original_source) { 'true' }

let(:location) do
Expand All @@ -26,7 +26,7 @@

let(:mutations) do
generated_nodes.map do |node|
Mutant::Mutation::Evil.new(subject: example, node:)
Mutant::Mutation::Evil.from_node(subject: example, node:)
end
end

Expand Down Expand Up @@ -145,7 +145,7 @@ def make_expected(input)
include_examples 'failure'
end

context 'when the mutation is invalid' do
context 'when the generated mutation is invalid' do
let(:invalid_node) do
s(:op_asgn, s(:send, s(:self), :at, s(:int, 1)), :+, s(:int, 1))
end
Expand All @@ -167,6 +167,13 @@ def make_expected(input)
Original: (operators: full)
(true)
true
Missing mutations:
s(:op_asgn,
s(:send,
s(:self), :at,
s(:int, 1)), :+,
s(:int, 1))
self.at(1) += 1
[invalid-mutation] report
[invalid-mutation] lines
REPORT
Expand All @@ -177,15 +184,18 @@ def make_expected(input)
end

before do
allow(Unparser::Validation).to receive_messages(from_node: validation)
allow(Mutant::Mutation).to receive_messages(from_node: left(validation))
end

include_examples 'failure'

it 'genrates validation with expected node' do
object.success?

expect(Unparser::Validation).to have_received(:from_node).with(invalid_node)
expect(Mutant::Mutation).to have_received(:from_node).with(
node: invalid_node,
subject: example
)
end
end
end
Loading

0 comments on commit 2c14a45

Please sign in to comment.