From 7408e86fd47c4d3d18e27dc72bb24379344ce8a6 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Mon, 29 Feb 2016 19:03:08 +0000 Subject: [PATCH] Fix coverage for Mutant::Meta namespace * Remove unparser invariant checking, does not yield enough value anymore as unparser is more stable these days --- config/flay.yml | 2 +- config/reek.yml | 3 +- lib/mutant/meta.rb | 12 +- lib/mutant/meta/example.rb | 115 ----------------- lib/mutant/meta/example/dsl.rb | 23 ++-- lib/mutant/meta/example/verification.rb | 86 +++++++++++++ spec/spec_helper.rb | 1 - spec/unit/mutant/meta/example/dsl_spec.rb | 100 +++++++++++++++ .../mutant/meta/example/verification_spec.rb | 119 ++++++++++++++++++ spec/unit/mutant/meta/example_spec.rb | 31 +++++ 10 files changed, 357 insertions(+), 135 deletions(-) create mode 100644 lib/mutant/meta/example/verification.rb create mode 100644 spec/unit/mutant/meta/example/dsl_spec.rb create mode 100644 spec/unit/mutant/meta/example/verification_spec.rb create mode 100644 spec/unit/mutant/meta/example_spec.rb diff --git a/config/flay.yml b/config/flay.yml index 1fd9b4b4..bdea6a8b 100644 --- a/config/flay.yml +++ b/config/flay.yml @@ -1,3 +1,3 @@ --- threshold: 18 -total_score: 1182 +total_score: 1172 diff --git a/config/reek.yml b/config/reek.yml index f4f23c63..e9f9d852 100644 --- a/config/reek.yml +++ b/config/reek.yml @@ -69,7 +69,6 @@ TooManyMethods: enabled: true exclude: - Mutant::CLI - - Mutant::Meta::Example::Verification - Mutant::Mutator::Node - Mutant::Parallel::Master max_methods: 10 @@ -128,7 +127,7 @@ UtilityFunction: - Mutant::Integration::Null#call - Mutant::Integration::Rspec#parse_example - Mutant::Integration::Rspec#parse_expression # intentional, private - - Mutant::Meta::Example::Verification#format_mutation + - Mutant::Meta::Example::Verification#format_mutations # intentional, private - Mutant::Reporter::CLI::Format::Progressive#new_buffer - Mutant::Reporter::CLI::Printer::StatusProgressive#object # False positive calls super - Mutant::Repository::Diff#tracks? # intentional, private diff --git a/lib/mutant/meta.rb b/lib/mutant/meta.rb index 4dfb9d2d..ebd4c54d 100644 --- a/lib/mutant/meta.rb +++ b/lib/mutant/meta.rb @@ -3,6 +3,7 @@ module Mutant module Meta require 'mutant/meta/example' require 'mutant/meta/example/dsl' + require 'mutant/meta/example/verification' # Mutation example class Example @@ -15,15 +16,20 @@ module Mutant # @return [undefined] def self.add(&block) file = caller.first.split(':in', 2).first - ALL << DSL.run(file, block) + ALL << DSL.call(file, block) end - Pathname.glob(Pathname.new(__FILE__).parent.parent.parent.join('meta', '**/*.rb')) + Pathname.glob(Pathname.new(__dir__).parent.parent.join('meta', '*.rb')) .sort .each(&method(:require)) + ALL.freeze - end # Example + # Remove mutation method only present for DSL executions from meta/**/*.rb + class << self + undef_method :add + end + end # Example end # Meta end # Mutant diff --git a/lib/mutant/meta/example.rb b/lib/mutant/meta/example.rb index 39c4de79..963236f0 100644 --- a/lib/mutant/meta/example.rb +++ b/lib/mutant/meta/example.rb @@ -27,121 +27,6 @@ module Mutant end end memoize :generated - - # Example verification - class Verification - include Adamantium::Flat, Concord.new(:example, :mutations) - - # Test if mutation was verified successfully - # - # @return [Boolean] - def success? - unparser.success? && missing.empty? && unexpected.empty? && no_diffs.empty? - end - - # Error report - # - # @return [String] - def error_report - unless unparser.success? - return unparser.report - end - mutation_report - end - - private - - # Unexpected mutations - # - # @return [Array] - def unexpected - mutations.map(&:node) - example.mutations - end - memoize :unexpected - - # Mutations with no diff to original - # - # @return [Enumerable] - def no_diffs - mutations.select { |mutation| mutation.source.eql?(example.source) } - end - memoize :no_diffs - - # Mutation report - # - # @return [String] - def mutation_report - original_node = example.node - [ - "#{example.file}, Original-AST:", - original_node.inspect, - 'Original-Source:', - example.source, - *missing_report, - *unexpected_report, - *no_diff_report - ].join("\n======\n") - end - - # Missing mutation report - # - # @return [Array, nil] - def missing_report - [ - 'Missing mutations:', - missing.map(&method(:format_mutation)).join("\n-----\n") - ] if missing.any? - end - - # No diff mutation report - # - # @return [Array, nil] - def no_diff_report - [ - 'No source diffs to original:', - no_diffs.map do |mutation| - "#{mutation.node.inspect}\n#{mutation.source}" - end - ] if no_diffs.any? - end - - # Unexpected mutation report - # - # @return [Array, nil] - def unexpected_report - [ - 'Unexpected mutations:', - unexpected.map(&method(:format_mutation)).join("\n-----\n") - ] if unexpected.any? - end - - # Format mutation - # - # @return [String] - def format_mutation(node) - [ - node.inspect, - Unparser.unparse(node) - ].join("\n") - end - - # Missing mutations - # - # @return [Array] - def missing - example.mutations - mutations.map(&:node) - end - memoize :missing - - # Unparser verifier - # - # @return [Unparser::CLI::Source] - def unparser - Unparser::CLI::Source::Node.new(Unparser::Preprocessor.run(example.node)) - end - memoize :unparser - - end # Verification end # Example end # Meta end # Mutant diff --git a/lib/mutant/meta/example/dsl.rb b/lib/mutant/meta/example/dsl.rb index e5ac5b06..47a3859e 100644 --- a/lib/mutant/meta/example/dsl.rb +++ b/lib/mutant/meta/example/dsl.rb @@ -1,7 +1,6 @@ module Mutant module Meta class Example - # Example DSL class DSL include AST::Sexp @@ -9,18 +8,20 @@ module Mutant # Run DSL on block # # @return [Example] - def self.run(file, block) + def self.call(file, block) instance = new(file) instance.instance_eval(&block) instance.example end - # Initialize DSL context + private_class_method :new + + # Initialize object # # @return [undefined] def initialize(file) - @file = file - @source = nil + @file = file + @source = nil @expected = [] end @@ -41,27 +42,23 @@ module Mutant # # @param [String,Parser::AST::Node] input # - # @return [self] + # @return [undefined] def source(input) fail 'source already defined' if @source @source = node(input) - - self end # Add expected mutation # # @param [String,Parser::AST::Node] input # - # @return [self] + # @return [undefined] def mutation(input) node = node(input) if @expected.include?(node) - fail "Node for input: #{input.inspect} is already expected" + fail "Mutation for input: #{input.inspect} is already expected" end @expected << node - - self end # Add singleton mutations @@ -87,7 +84,7 @@ module Mutant when ::Parser::AST::Node input else - fail "Cannot coerce to node: #{source.inspect}" + fail "Cannot coerce to node: #{input.inspect}" end end diff --git a/lib/mutant/meta/example/verification.rb b/lib/mutant/meta/example/verification.rb new file mode 100644 index 00000000..55549512 --- /dev/null +++ b/lib/mutant/meta/example/verification.rb @@ -0,0 +1,86 @@ +module Mutant + module Meta + class Example + # Example verification + class Verification + include Adamantium::Flat, Concord.new(:example, :mutations) + + # Test if mutation was verified successfully + # + # @return [Boolean] + def success? + missing.empty? && unexpected.empty? && no_diffs.empty? + end + + # Error report + # + # @return [String] + def error_report + fail 'no error report on successful validation' if success? + + YAML.dump( + 'file' => example.file, + 'original_ast' => example.node.inspect, + 'original_source' => example.source, + 'missing' => format_mutations(missing), + 'unexpected' => format_mutations(unexpected), + 'no_diff' => no_diff_report + ) + end + + private + + # Unexpected mutations + # + # @return [Array] + def unexpected + mutations.map(&:node) - example.mutations + end + memoize :unexpected + + # Mutations with no diff to original + # + # @return [Enumerable] + def no_diffs + mutations.select { |mutation| mutation.source.eql?(example.source) } + end + memoize :no_diffs + + # Mutation report + # + # @param [Array] nodes + # + # @return [Array] + def format_mutations(nodes) + nodes.map do |node| + { + 'node' => node.inspect, + 'source' => Unparser.unparse(node) + } + end + end + + # No diff mutation report + # + # @return [Array, nil] + def no_diff_report + no_diffs.map do |mutation| + { + 'node' => mutation.node.inspect, + 'source' => mutation.source + } + end + end + + # Missing mutations + # + # @return [Array] + def missing + example.mutations - mutations.map(&:node) + end + memoize :missing + + end # Verification + end # Example + end # Meta +end # Mutant diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 30cc770e..ddda7f1c 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -9,7 +9,6 @@ if ENV['COVERAGE'] == 'true' add_filter 'vendor' add_filter 'test_app' add_filter 'lib/mutant.rb' # simplecov bug not seeing default block is executed - add_filter 'lib/mutant/meta/*' add_filter 'lib/mutant/zombifier' add_filter 'lib/mutant/zombifier/*' # Trace points shadow each other under 2.0 (fixed in 2.1) diff --git a/spec/unit/mutant/meta/example/dsl_spec.rb b/spec/unit/mutant/meta/example/dsl_spec.rb new file mode 100644 index 00000000..785fd6bb --- /dev/null +++ b/spec/unit/mutant/meta/example/dsl_spec.rb @@ -0,0 +1,100 @@ +RSpec.describe Mutant::Meta::Example::DSL do + describe '.call' do + subject { described_class.call(file, block) } + + let(:file) { 'foo.rb' } + let(:node) { s(:false) } + let(:expected) { [] } + + let(:expected_example) do + Mutant::Meta::Example.new(file, node, expected) + end + + def self.expect_example(&block) + let(:block) { block } + + specify do + # Kill mutations to warnings + warnings = Mutant::WarningFilter.use do + should eql(expected_example) + end + expect(warnings).to eql([]) + end + end + + def self.expect_error(message, &block) + let(:block) { block } + + specify do + expect { subject }.to raise_error(RuntimeError, message) + end + end + + context 'source as node' do + expect_example do + source s(:false) + end + end + + context 'source as string' do + expect_example do + source 'false' + end + end + + context 'on node that needs unparser preprocessing to be normalized' do + let(:node) { s(:send, s(:float, -1.0), :/, s(:float, 0.0)) } + + expect_example do + source '(-1.0) / 0.0' + end + end + + context 'using #mutation' do + let(:expected) { [s(:nil)] } + + expect_example do + source 'false' + + mutation 'nil' + end + end + + context 'using #singleton_mutations' do + let(:expected) { [s(:nil), s(:self)] } + + expect_example do + source 'false' + + singleton_mutations + end + end + + context 'no definition of source' do + expect_error('source not defined') do + end + end + + context 'duplicate definition of source' do + expect_error('source already defined') do + source 'true' + source 'false' + end + end + + context 'uncoercable source' do + expect_error('Cannot coerce to node: nil') do + source nil + end + end + + context 'duplicate mutation expectation' do + expect_error('Mutation for input: "true" is already expected') do + source 'false' + + mutation 'true' + mutation 'true' + end + end + end +end diff --git a/spec/unit/mutant/meta/example/verification_spec.rb b/spec/unit/mutant/meta/example/verification_spec.rb new file mode 100644 index 00000000..36d345e2 --- /dev/null +++ b/spec/unit/mutant/meta/example/verification_spec.rb @@ -0,0 +1,119 @@ +RSpec.describe Mutant::Meta::Example::Verification do + let(:object) { described_class.new(example, mutations) } + + let(:example) do + Mutant::Meta::Example.new( + 'foo.rb', + s(:true), + expected_nodes + ) + end + + let(:mutations) do + generated_nodes.map do |node| + Mutant::Mutation::Evil.new(example, node) + end + end + + let(:generated_nodes) { [] } + let(:expected_nodes) { [] } + + describe '#success?' do + subject { object.success? } + + context 'when generated nodes equal expected nodes' do + it { should be(true) } + end + + context 'when expected node is missing' do + let(:expected_nodes) { [s(:false)] } + + it { should be(false) } + end + + context 'when there is extra generated node' do + let(:generated_nodes) { [s(:false)] } + + it { should be(false) } + end + + context 'when there is no diff to original source' do + let(:expected_nodes) { [s(:true)] } + let(:generated_nodes) { [s(:true)] } + + it { should be(false) } + end + end + + describe '#error_report' do + subject { object.error_report } + + context 'on success' do + specify do + expect { subject }.to raise_error( + RuntimeError, + 'no error report on successful validation' + ) + end + end + + context 'when expected node is missing' do + let(:expected_nodes) { [s(:false), s(:nil)] } + + specify do + should eql(strip_indent(<<-'REPORT')) + --- + file: foo.rb + original_ast: s(:true) + original_source: 'true' + missing: + - node: s(:false) + source: 'false' + - node: s(:nil) + source: nil + unexpected: [] + no_diff: [] + REPORT + end + end + + context 'when there is extra generated node' do + let(:generated_nodes) { [s(:false), s(:nil)] } + + specify do + should eql(strip_indent(<<-'REPORT')) + --- + file: foo.rb + original_ast: s(:true) + original_source: 'true' + missing: [] + unexpected: + - node: s(:false) + source: 'false' + - node: s(:nil) + source: nil + no_diff: [] + REPORT + end + end + + context 'when there is no diff to original source' do + let(:expected_nodes) { [s(:true)] } + let(:generated_nodes) { [s(:true)] } + + specify do + should eql(strip_indent(<<-'REPORT')) + --- + file: foo.rb + original_ast: s(:true) + original_source: 'true' + missing: [] + unexpected: [] + no_diff: + - node: s(:true) + source: 'true' + REPORT + end + end + end +end diff --git a/spec/unit/mutant/meta/example_spec.rb b/spec/unit/mutant/meta/example_spec.rb new file mode 100644 index 00000000..b3063446 --- /dev/null +++ b/spec/unit/mutant/meta/example_spec.rb @@ -0,0 +1,31 @@ +RSpec.describe Mutant::Meta::Example do + let(:object) do + described_class.new( + file, + node, + mutation_nodes + ) + end + + let(:file) { 'foo/bar.rb' } + let(:node) { s(:true) } + let(:mutation_nodes) { [s(:nil), s(:false)] } + + let(:mutations) do + mutation_nodes.map do |node| + Mutant::Mutation::Evil.new(object, node) + end + end + + describe '#source' do + subject { object.source } + + it { should eql('true') } + end + + describe '#verification' do + subject { object.verification } + + it { should eql(Mutant::Meta::Example::Verification.new(object, mutations)) } + end +end