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 util subcommand #1249

Merged
merged 1 commit into from
Aug 25, 2021
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
7 changes: 7 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
# v0.10.33 2021-08-25

* [#1249](https://github.com/mbj/mutant/pull/1249/files)
Add `mutant util mutation` subcommand to allow inspect mutations of
a code snippet outside a booted environment.
This eases debugging, learning and mutant developers life.

# v0.10.32 2021-05-16

* [#1235](https://github.com/mbj/mutant/pull/1235)
Expand Down
2 changes: 2 additions & 0 deletions lib/mutant.rb
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ module Mutant
require 'mutant/cli/command/environment/show'
require 'mutant/cli/command/environment/subject'
require 'mutant/cli/command/environment/test'
require 'mutant/cli/command/util'
require 'mutant/cli/command/root'
require 'mutant/runner'
require 'mutant/runner/sink'
Expand All @@ -212,6 +213,7 @@ module Mutant
require 'mutant/reporter/cli/printer/env_progress'
require 'mutant/reporter/cli/printer/env_result'
require 'mutant/reporter/cli/printer/isolation_result'
require 'mutant/reporter/cli/printer/mutation'
require 'mutant/reporter/cli/printer/mutation_result'
require 'mutant/reporter/cli/printer/status_progressive'
require 'mutant/reporter/cli/printer/subject_result'
Expand Down
2 changes: 2 additions & 0 deletions lib/mutant/cli/command.rb
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ def zombie?
false
end

abstract_method :action

private

def subcommands
Expand Down
2 changes: 1 addition & 1 deletion lib/mutant/cli/command/root.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ class Environment < self
class Root < self
NAME = 'mutant'
SHORT_DESCRIPTION = 'mutation testing engine main command'
SUBCOMMANDS = [Environment::Run, Environment, Subscription].freeze
SUBCOMMANDS = [Environment::Run, Environment, Subscription, Util].freeze
end # Root
end # Command
end # CLI
Expand Down
100 changes: 100 additions & 0 deletions lib/mutant/cli/command/util.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
# frozen_string_literal: true

module Mutant
module CLI
class Command
class Util < self
NAME = 'util'
SHORT_DESCRIPTION = 'Utility subcommands'

class Mutation < self
NAME = 'mutation'
SHORT_DESCRIPTION = 'Print mutations of a code snippet'
SUBCOMMANDS = [].freeze
OPTIONS = %i[add_target_options].freeze

def action
@targets.each(&method(:print_mutations))
Either::Right.new(nil)
end

private

class Target
include Adamantium

def node
Unparser.parse(source)
end
memoize :node

class File < self
include Concord.new(:pathname, :source)

public :source

def identification
"file:#{pathname}"
end
end # File

class Source < self
include Concord::Public.new(:source)

def identification
'<cli-source>'
end
end # source
end # Target

def initialize(_arguments)
super

@targets = []
end

def add_target_options(parser)
parser.on('-e', '--evaluate SOURCE') do |source|
@targets << Target::Source.new(source)
end
end

def print_mutations(target)
world.stdout.puts(target.identification)
Mutator.mutate(target.node).each do |mutation|
Reporter::CLI::Printer::Mutation.call(
world.stdout,
Mutant::Mutation::Evil.new(target, mutation)
)
end
end

def parse_remaining_arguments(arguments)
@targets.concat(
arguments.map do |argument|
parse_pathname(argument)
.bind(&method(:read_file))
.from_right { |error| return Either::Left.new(error) }
end
)

Either::Right.new(self)
end

def read_file(pathname)
Either::Right.new(Target::File.new(pathname, pathname.read))
rescue StandardError => exception
Either::Left.new("Cannot read file: #{exception}")
end

def parse_pathname(input)
Either.wrap_error(ArgumentError) { Pathname.new(input) }
.lmap(&:message)
end
end # Mutation

SUBCOMMANDS = [Mutation].freeze
end # Util
end # Command
end # CLI
end # Mutant
10 changes: 9 additions & 1 deletion lib/mutant/mutation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,15 @@ def insert(kernel)
)
end

# Rendered mutation diff
#
# @return [String, nil]
# the diff, if present
def diff
Unparser::Diff.build(original_source, source)
end
memoize :diff

private

def sha1
Expand All @@ -80,7 +89,6 @@ def sha1

# Evil mutation that should case mutations to fail tests
class Evil < self

SYMBOL = 'evil'
TEST_PASS_SUCCESS = false

Expand Down
58 changes: 58 additions & 0 deletions lib/mutant/reporter/cli/printer/mutation.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# frozen_string_literal: true

module Mutant
class Reporter
class CLI
class Printer
# Reporter for mutations
class Mutation < self
NO_DIFF_MESSAGE = <<~'MESSAGE'
--- Internal failure ---
BUG: A generted mutation did not result in exactly one diff hunk!
This is an invariant violation by the mutation generation engine.
Please report a reproduction to https://github.com/mbj/mutant
Original unparsed source:
%s
Original AST:
%s
Mutated unparsed source:
%s
Mutated AST:
%s
MESSAGE

SEPARATOR = '-----------------------'

# Run report printer
#
# @return [undefined]
def run
diff = object.diff
diff = color? ? diff.colorized_diff : diff.diff

if diff
output.write(diff)
else
print_no_diff_message
end
end

def print_no_diff_message
info(
NO_DIFF_MESSAGE,
object.original_source,
original_node.inspect,
object.source,
object.node.inspect
)
end

def original_node
object.subject.node
end

end # MutationResult
end # Printer
end # CLI
end # Reporter
end # Mutant
24 changes: 2 additions & 22 deletions lib/mutant/reporter/cli/printer/mutation_result.rb
Original file line number Diff line number Diff line change
Expand Up @@ -66,35 +66,15 @@ def print_details
end

def evil_details
diff = Unparser::Diff.build(mutation.original_source, mutation.source)
diff = color? ? diff.colorized_diff : diff.diff
if diff
output.write(diff)
else
print_no_diff_message
end
end

def print_no_diff_message
info(
NO_DIFF_MESSAGE,
mutation.original_source,
original_node.inspect,
mutation.source,
mutation.node.inspect
)
visit(Mutation, mutation)
end

def noop_details
info(NOOP_MESSAGE)
end

def neutral_details
info(NEUTRAL_MESSAGE, original_node.inspect, mutation.source)
end

def original_node
mutation.subject.node
info(NEUTRAL_MESSAGE, mutation.node.inspect, mutation.source)
end

end # MutationResult
Expand Down
Loading