diff --git a/config/flay.yml b/config/flay.yml index bc67177d..7d97443b 100644 --- a/config/flay.yml +++ b/config/flay.yml @@ -1,3 +1,3 @@ --- threshold: 18 -total_score: 1237 +total_score: 1240 diff --git a/config/flog.yml b/config/flog.yml index a3574a02..2f43979a 100644 --- a/config/flog.yml +++ b/config/flog.yml @@ -1,2 +1,2 @@ --- -threshold: 30.9 +threshold: 29.3 diff --git a/lib/mutant.rb b/lib/mutant.rb index 37ea6398..af99497a 100644 --- a/lib/mutant.rb +++ b/lib/mutant.rb @@ -217,6 +217,16 @@ require 'mutant/reporter/null' require 'mutant/reporter/trace' require 'mutant/reporter/cli' require 'mutant/reporter/cli/printer' +require 'mutant/reporter/cli/printer/config' +require 'mutant/reporter/cli/printer/env_result' +require 'mutant/reporter/cli/printer/env_progress' +require 'mutant/reporter/cli/printer/mutation_result' +require 'mutant/reporter/cli/printer/mutation_progress_result' +require 'mutant/reporter/cli/printer/subject_progress' +require 'mutant/reporter/cli/printer/subject_result' +require 'mutant/reporter/cli/printer/status' +require 'mutant/reporter/cli/printer/status_progressive' +require 'mutant/reporter/cli/printer/test_result' require 'mutant/reporter/cli/tput' require 'mutant/reporter/cli/format' require 'mutant/zombifier' diff --git a/lib/mutant/reporter/cli.rb b/lib/mutant/reporter/cli.rb index ee58709f..45146502 100644 --- a/lib/mutant/reporter/cli.rb +++ b/lib/mutant/reporter/cli.rb @@ -53,11 +53,6 @@ module Mutant # # TODO: Move this to a callback registration # - # Reporters other than CLI that might exist in future - # may only need the final report. So providing a noop callback - # registration makes more sense for these. - # As only CLI reporters exist currently I do not really care right now. - # # @return [Float] # # @api private @@ -88,7 +83,7 @@ module Mutant # @api private # def report(env) - Printer::EnvResult.run(output, env) + Printer::EnvResult.call(output, env) self end diff --git a/lib/mutant/reporter/cli/format.rb b/lib/mutant/reporter/cli/format.rb index 3b29b19a..51534da6 100644 --- a/lib/mutant/reporter/cli/format.rb +++ b/lib/mutant/reporter/cli/format.rb @@ -69,7 +69,7 @@ module Mutant # def format(printer, object) buffer = new_buffer - printer.run(Output.new(tty, buffer), object) + printer.call(Output.new(tty, buffer), object) buffer.rewind buffer.read end diff --git a/lib/mutant/reporter/cli/printer.rb b/lib/mutant/reporter/cli/printer.rb index 70f7a262..87b745de 100644 --- a/lib/mutant/reporter/cli/printer.rb +++ b/lib/mutant/reporter/cli/printer.rb @@ -3,25 +3,14 @@ module Mutant class CLI # CLI runner status printer base class class Printer - include AbstractType, Delegator, Adamantium::Flat, Concord.new(:output, :object) + include AbstractType, Delegator, Adamantium::Flat, Concord.new(:output, :object), Procto.call(:run) - delegate(:success?) + private_class_method :new + + delegate :success? NL = "\n".freeze - # Run printer on object to output - # - # @param [IO] output - # @param [Object] object - # - # @return [self] - # - # @api private - # - def self.run(output, object) - new(output, object).run - end - # Run printer # # @return [self] @@ -67,7 +56,7 @@ module Mutant # @api private # def visit(printer, object) - printer.run(output, object) + printer.call(output, object) end # Print an info line to output @@ -133,490 +122,6 @@ module Mutant # @api private # alias_method :color?, :tty? - - # Printer for runner status - class Status < self - - delegate(:active_jobs, :payload) - - # Print progress for collector - # - # @return [self] - # - # @api private - # - def run - visit(EnvProgress, payload) - job_status - info('Active subjects: %d', active_subject_results.length) - visit_collection(SubjectProgress, active_subject_results) - self - end - - private - - # Print worker status - # - # @return [undefined] - # - # @api private - # - def job_status - return if active_jobs.empty? - info('Active Jobs:') - active_jobs.sort_by(&:index).each do |job| - info('%d: %s', job.index, job.payload.identification) - end - end - - # Return active subject results - # - # @return [Array] - # - # @api private - # - def active_subject_results - active_mutation_jobs = active_jobs.select { |job| job.payload.kind_of?(Mutation) } - active_subjects = active_mutation_jobs.map(&:payload).flat_map(&:subject).to_set - - payload.subject_results.select do |subject_result| - active_subjects.include?(subject_result.subject) - end - end - - end # Status - - # Progress printer for configuration - class Config < self - - # Report configuration - # - # @param [Mutant::Config] config - # - # @return [self] - # - # @api private - # - # rubocop:disable AbcSize - # - def run - info 'Mutant configuration:' - info 'Matcher: %s', object.matcher.inspect - info 'Integration: %s', object.integration.name - info 'Expect Coverage: %0.2f%%', (object.expected_coverage * 100) - info 'Jobs: %d', object.jobs - info 'Includes: %s', object.includes.inspect - info 'Requires: %s', object.requires.inspect - self - end - - end # Config - - # Env progress printer - class EnvProgress < self - - delegate( - :coverage, - :amount_subjects, - :amount_mutations, - :amount_mutations_alive, - :amount_mutations_killed, - :runtime, - :killtime, - :overhead, - :env - ) - - # Run printer - # - # @return [self] - # - # @api private - # - # rubocop:disable MethodLength - # - def run - visit(Config, env.config) - info 'Subjects: %s', amount_subjects - info 'Mutations: %s', amount_mutations - info 'Kills: %s', amount_mutations_killed - info 'Alive: %s', amount_mutations_alive - info 'Runtime: %0.2fs', runtime - info 'Killtime: %0.2fs', killtime - info 'Overhead: %0.2f%%', overhead_percent - status 'Coverage: %0.2f%%', coverage_percent - status 'Expected: %0.2f%%', (env.config.expected_coverage * 100) - self - end - - private - - # Return coverage percent - # - # @return [Float] - # - # @api private - # - def coverage_percent - coverage * 100 - end - - # Return overhead percent - # - # @return [Float] - # - # @api private - # - def overhead_percent - (overhead / killtime) * 100 - end - - end # EnvProgress - - # Full env result reporter - class EnvResult < self - - delegate(:failed_subject_results) - - # Run printer - # - # @return [self] - # - # @api private - # - def run - visit_collection(SubjectResult, failed_subject_results) - visit(EnvProgress, object) - self - end - - end # EnvResult - - # Subject report printer - class SubjectResult < self - - delegate :subject, :failed_mutations, :tests - - # Run report printer - # - # @return [self] - # - # @api private - # - def run - status(subject.identification) - tests.each do |test| - puts("- #{test.identification}") - end - visit_collection(MutationResult, object.alive_mutation_results) - self - end - - end # Subject - - # Printer for mutation progress results - class MutationProgressResult < self - - SUCCESS = '.'.freeze - FAILURE = 'F'.freeze - - # Run printer - # - # @return [self] - # - # @api private - # - def run - char(success? ? SUCCESS : FAILURE) - end - - private - - # Write colorized char - # - # @param [String] char - # - # @return [undefined] - # - # @api private - # - def char(char) - output.write(colorize(status_color, char)) - end - - end # MutationProgressResult - - # Reporter for progressive output format on scheduler Status objects - class StatusProgressive < self - - FORMAT = '(%02d/%02d) %3d%% - killtime: %0.02fs runtime: %0.02fs overhead: %0.02fs'.freeze - - delegate( - :coverage, - :runtime, - :amount_mutations_killed, - :amount_mutations, - :amount_mutation_results, - :killtime, - :overhead - ) - - # Run printer - # - # @return [self] - # - # @api private - # - def run - status( - FORMAT, - amount_mutations_killed, - amount_mutations, - coverage * 100, - killtime, - runtime, - overhead - ) - - self - end - - private - - # Return object being printed - # - # @return [Result::Env] - # - # @api private - # - def object - super().payload - end - end - - # Reporter for subject progress - class SubjectProgress < self - - FORMAT = '(%02d/%02d) %3d%% - killtime: %0.02fs runtime: %0.02fs overhead: %0.02fs'.freeze - - delegate( - :tests, - :subject, - :coverage, - :runtime, - :amount_mutations_killed, - :amount_mutations, - :amount_mutation_results, - :killtime, - :overhead - ) - - # Run printer - # - # @return [self] - # - # @api private - # - def run - puts("#{subject.identification} mutations: #{amount_mutations}") - print_mutation_results - print_progress_bar_finish - print_stats - print_tests - self - end - - private - - # Print stats - # - # @return [undefined] - # - # @api private - # - def print_stats - status( - FORMAT, - amount_mutations_killed, - amount_mutations, - coverage * 100, - killtime, - runtime, - overhead - ) - end - - # Print tests - # - # @return [undefined] - # - # @api private - # - def print_tests - tests.each do |test| - puts "- #{test.identification}" - end - end - - # Print progress bar finish - # - # @return [undefined] - # - # @api private - # - def print_progress_bar_finish - puts(NL) unless amount_mutation_results.zero? - end - - # Print mutation results - # - # @return [undefined] - # - # @api private - # - def print_mutation_results - visit_collection(MutationProgressResult, object.mutation_results) - end - - end # Subject - - # Reporter for mutation results - class MutationResult < self - - delegate :mutation, :test_result - - DIFF_ERROR_MESSAGE = - 'BUG: Mutation NOT resulted in exactly one diff hunk. Please report a reproduction!'.freeze - - MAP = { - Mutant::Mutation::Evil => :evil_details, - Mutant::Mutation::Neutral => :neutral_details, - Mutant::Mutation::Noop => :noop_details - }.freeze - - NEUTRAL_MESSAGE = - "--- Neutral failure ---\n" \ - "Original code was inserted unmutated. And the test did NOT PASS.\n" \ - "Your tests do not pass initially or you found a bug in mutant / unparser.\n" \ - "Subject AST:\n" \ - "%s\n" \ - "Unparsed Source:\n" \ - "%s\n" \ - "Test Result:\n".freeze - - NOOP_MESSAGE = - "---- Noop failure -----\n" \ - "No code was inserted. And the test did NOT PASS.\n" \ - "This is typically a problem of your specs not passing unmutated.\n" \ - "Test Result:\n".freeze - - FOOTER = '-----------------------'.freeze - - # Run report printer - # - # @return [self] - # - # @api private - # - def run - puts(mutation.identification) - print_details - puts(FOOTER) - self - end - - private - - # Return details - # - # @return [undefined] - # - # @api private - # - def print_details - send(MAP.fetch(mutation.class)) - end - - # Return evil details - # - # @return [String] - # - # @api private - # - def evil_details - original, current = mutation.original_source, mutation.source - diff = Mutant::Diff.build(original, current) - diff = color? ? diff.colorized_diff : diff.diff - puts(diff || ['Original source:', original, 'Mutated Source:', current, DIFF_ERROR_MESSAGE]) - end - - # Noop details - # - # @return [String] - # - # @api private - # - def noop_details - info(NOOP_MESSAGE) - visit_test_result - end - - # Neutral details - # - # @return [String] - # - # @api private - # - def neutral_details - info(NEUTRAL_MESSAGE, mutation.subject.node.inspect, mutation.source) - visit_test_result - end - - # Visit failed test results - # - # @return [undefined] - # - # @api private - # - def visit_test_result - visit(TestResult, test_result) - end - - end # MutationResult - - # Test result reporter - class TestResult < self - - delegate :tests, :runtime - - # Run test result reporter - # - # @return [self] - # - # @api private - # - def run - status('- %d @ runtime: %s', tests.length, runtime) - tests.each do |test| - puts(" - #{test.identification}") - end - puts('Test Output:') - puts(object.output) - end - - # Test if test result is successful - # - # Only used to determine color. - # - # @return [false] - # - # @api private - # - def success? - false - end - - end # TestResult end # Printer end # CLI end # Reporter diff --git a/lib/mutant/reporter/cli/printer/config.rb b/lib/mutant/reporter/cli/printer/config.rb new file mode 100644 index 00000000..fbbbf5de --- /dev/null +++ b/lib/mutant/reporter/cli/printer/config.rb @@ -0,0 +1,32 @@ +module Mutant + class Reporter + class CLI + class Printer + # Printer for mutation config + class Config < self + + # Report configuration + # + # @param [Mutant::Config] config + # + # @return [undefined] + # + # @api private + # + # rubocop:disable AbcSize + # + def run + info 'Mutant configuration:' + info 'Matcher: %s', object.matcher.inspect + info 'Integration: %s', object.integration.name + info 'Expect Coverage: %0.2f%%', (object.expected_coverage * 100) + info 'Jobs: %d', object.jobs + info 'Includes: %s', object.includes + info 'Requires: %s', object.requires + end + + end # Config + end # Printer + end # CLI + end # Reporter +end # Mutant diff --git a/lib/mutant/reporter/cli/printer/env_progress.rb b/lib/mutant/reporter/cli/printer/env_progress.rb new file mode 100644 index 00000000..bc7476df --- /dev/null +++ b/lib/mutant/reporter/cli/printer/env_progress.rb @@ -0,0 +1,66 @@ +module Mutant + class Reporter + class CLI + class Printer + # Env progress printer + class EnvProgress < self + delegate( + :coverage, + :amount_subjects, + :amount_mutations, + :amount_mutations_alive, + :amount_mutations_killed, + :runtime, + :killtime, + :overhead, + :env + ) + + # Run printer + # + # @return [undefined] + # + # @api private + # + # rubocop:disable MethodLength + # rubocop:disable AbcSize + # + def run + visit(Config, env.config) + info 'Subjects: %s', amount_subjects + info 'Mutations: %s', amount_mutations + info 'Kills: %s', amount_mutations_killed + info 'Alive: %s', amount_mutations_alive + info 'Runtime: %0.2fs', runtime + info 'Killtime: %0.2fs', killtime + info 'Overhead: %0.2f%%', overhead_percent + status 'Coverage: %0.2f%%', coverage_percent + status 'Expected: %0.2f%%', (env.config.expected_coverage * 100) + end + + private + + # Return coverage percent + # + # @return [Float] + # + # @api private + # + def coverage_percent + coverage * 100 + end + + # Return overhead percent + # + # @return [Float] + # + # @api private + # + def overhead_percent + (overhead / killtime) * 100 + end + end # EnvProgress + end # Printer + end # CLI + end # Reporter +end # Mutant diff --git a/lib/mutant/reporter/cli/printer/env_result.rb b/lib/mutant/reporter/cli/printer/env_result.rb new file mode 100644 index 00000000..34f778e1 --- /dev/null +++ b/lib/mutant/reporter/cli/printer/env_result.rb @@ -0,0 +1,23 @@ +module Mutant + class Reporter + class CLI + class Printer + # Full env result reporter + class EnvResult < self + delegate(:failed_subject_results) + + # Run printer + # + # @return [undefined] + # + # @api private + # + def run + visit_collection(SubjectResult, failed_subject_results) + visit(EnvProgress, object) + end + end # EnvResult + end # Printer + end # CLI + end # Reporter +end # Mutant diff --git a/lib/mutant/reporter/cli/printer/mutation_progress_result.rb b/lib/mutant/reporter/cli/printer/mutation_progress_result.rb new file mode 100644 index 00000000..799ada73 --- /dev/null +++ b/lib/mutant/reporter/cli/printer/mutation_progress_result.rb @@ -0,0 +1,37 @@ +module Mutant + class Reporter + class CLI + class Printer + # Printer for mutation progress results + class MutationProgressResult < self + SUCCESS = '.'.freeze + FAILURE = 'F'.freeze + + # Run printer + # + # @return [undefined] + # + # @api private + # + def run + char(success? ? SUCCESS : FAILURE) + end + + private + + # Write colorized char + # + # @param [String] char + # + # @return [undefined] + # + # @api private + # + def char(char) + output.write(colorize(status_color, char)) + end + end # MutationProgressResult + end # Printer + end # CLI + end # Reporter +end # Mutant diff --git a/lib/mutant/reporter/cli/printer/mutation_result.rb b/lib/mutant/reporter/cli/printer/mutation_result.rb new file mode 100644 index 00000000..f867fe96 --- /dev/null +++ b/lib/mutant/reporter/cli/printer/mutation_result.rb @@ -0,0 +1,151 @@ +module Mutant + class Reporter + class CLI + class Printer + # Reporter for mutation results + class MutationResult < self + + delegate :mutation, :test_result + + DIFF_ERROR_MESSAGE = + 'BUG: Mutation NOT resulted in exactly one diff hunk. Please report a reproduction!'.freeze + + MAP = { + Mutant::Mutation::Evil => :evil_details, + Mutant::Mutation::Neutral => :neutral_details, + Mutant::Mutation::Noop => :noop_details + }.freeze + + NEUTRAL_MESSAGE = + "--- Neutral failure ---\n" \ + "Original code was inserted unmutated. And the test did NOT PASS.\n" \ + "Your tests do not pass initially or you found a bug in mutant / unparser.\n" \ + "Subject AST:\n" \ + "%s\n" \ + "Unparsed Source:\n" \ + "%s\n" \ + "Test Result:\n".freeze + + NO_DIFF_MESSAGE = + "--- Internal failure ---\n" \ + "BUG: Mutation NOT resulted in exactly one diff hunk. Please report a reproduction!\n" \ + "Original unparsed source:\n" \ + "%s\n" \ + "Original AST:\n" \ + "%s\n" \ + "Mutated unparsed source:\n" \ + "%s\n" \ + "Mutated AST:\n" \ + "%s\n".freeze + + NOOP_MESSAGE = + "---- Noop failure -----\n" \ + "No code was inserted. And the test did NOT PASS.\n" \ + "This is typically a problem of your specs not passing unmutated.\n" \ + "Test Result:\n".freeze + + FOOTER = '-----------------------'.freeze + + # Run report printer + # + # @return [undefined] + # + # @api private + # + def run + puts(mutation.identification) + print_details + puts(FOOTER) + end + + private + + # Return details + # + # @return [undefined] + # + # @api private + # + def print_details + __send__(MAP.fetch(mutation.class)) + end + + # Return evil details + # + # @return [String] + # + # @api private + # + def evil_details + diff = 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 + + # Print no diff message + # + # @return [undefined] + # + # @api private + # + def print_no_diff_message + info( + NO_DIFF_MESSAGE, + mutation.original_source, + original_node.inspect, + mutation.source, + mutation.node.inspect + ) + end + + # Noop details + # + # @return [String] + # + # @api private + # + def noop_details + info(NOOP_MESSAGE) + visit_test_result + end + + # Neutral details + # + # @return [String] + # + # @api private + # + def neutral_details + info(NEUTRAL_MESSAGE, original_node.inspect, mutation.source) + visit_test_result + end + + # Visit failed test results + # + # @return [undefined] + # + # @api private + # + def visit_test_result + visit(TestResult, test_result) + end + + # Return original node + # + # @return [Parser::AST::Node] + # + # @api private + # + def original_node + mutation.subject.node + end + + end # MutationResult + end # Printer + end # CLI + end # Reporter +end # Mutant diff --git a/lib/mutant/reporter/cli/printer/status.rb b/lib/mutant/reporter/cli/printer/status.rb new file mode 100644 index 00000000..7c505677 --- /dev/null +++ b/lib/mutant/reporter/cli/printer/status.rb @@ -0,0 +1,60 @@ +module Mutant + class Reporter + class CLI + class Printer + # Printer for runner status + class Status < self + + delegate(:active_jobs, :payload) + + ACTIVE_JOB_HEADER = 'Active Jobs:'.freeze + ACTIVE_JOB_FORMAT = '%d: %s'.freeze + + # Print progress for collector + # + # @return [undefined] + # + # @api private + # + def run + visit(EnvProgress, payload) + job_status + info('Active subjects: %d', active_subject_results.length) + visit_collection(SubjectProgress, active_subject_results) + end + + private + + # Print worker status + # + # @return [undefined] + # + # @api private + # + def job_status + return if active_jobs.empty? + info(ACTIVE_JOB_HEADER) + active_jobs.sort_by(&:index).each do |job| + info(ACTIVE_JOB_FORMAT, job.index, job.payload.identification) + end + end + + # Return active subject results + # + # @return [Array] + # + # @api private + # + def active_subject_results + active_subjects = active_jobs.map(&:payload).flat_map(&:subject) + + payload.subject_results.select do |subject_result| + active_subjects.include?(subject_result.subject) + end + end + + end # Status + end # Printer + end # CLI + end # Reporter +end # Mutant diff --git a/lib/mutant/reporter/cli/printer/status_progressive.rb b/lib/mutant/reporter/cli/printer/status_progressive.rb new file mode 100644 index 00000000..00787570 --- /dev/null +++ b/lib/mutant/reporter/cli/printer/status_progressive.rb @@ -0,0 +1,52 @@ +module Mutant + class Reporter + class CLI + class Printer + # Reporter for progressive output format on scheduler Status objects + class StatusProgressive < self + FORMAT = '(%02d/%02d) %3d%% - killtime: %0.02fs runtime: %0.02fs overhead: %0.02fs'.freeze + + delegate( + :coverage, + :runtime, + :amount_mutations_killed, + :amount_mutations, + :amount_mutation_results, + :killtime, + :overhead + ) + + # Run printer + # + # @return [undefined] + # + # @api private + # + def run + status( + FORMAT, + amount_mutations_killed, + amount_mutations, + coverage * 100, + killtime, + runtime, + overhead + ) + end + + private + + # Return object being printed + # + # @return [Result::Env] + # + # @api private + # + def object + super.payload + end + end # StatusProgressive + end # Printer + end # CLI + end # Reporter +end # Mutant diff --git a/lib/mutant/reporter/cli/printer/subject_progress.rb b/lib/mutant/reporter/cli/printer/subject_progress.rb new file mode 100644 index 00000000..2e30d3e9 --- /dev/null +++ b/lib/mutant/reporter/cli/printer/subject_progress.rb @@ -0,0 +1,90 @@ +module Mutant + class Reporter + class CLI + class Printer + # Reporter for subject progress + class SubjectProgress < self + FORMAT = '(%02d/%02d) %3d%% - killtime: %0.02fs runtime: %0.02fs overhead: %0.02fs'.freeze + + delegate( + :tests, + :subject, + :coverage, + :runtime, + :amount_mutations_killed, + :amount_mutations, + :amount_mutation_results, + :killtime, + :overhead + ) + + # Run printer + # + # @return [undefined] + # + # @api private + # + def run + puts("#{subject.identification} mutations: #{amount_mutations}") + print_mutation_results + print_progress_bar_finish + print_stats + print_tests + end + + private + + # Print stats + # + # @return [undefined] + # + # @api private + # + def print_stats + status( + FORMAT, + amount_mutations_killed, + amount_mutations, + coverage * 100, + killtime, + runtime, + overhead + ) + end + + # Print tests + # + # @return [undefined] + # + # @api private + # + def print_tests + tests.each do |test| + puts "- #{test.identification}" + end + end + + # Print progress bar finish + # + # @return [undefined] + # + # @api private + # + def print_progress_bar_finish + puts(nil) unless amount_mutation_results.zero? + end + + # Print mutation results + # + # @return [undefined] + # + # @api private + # + def print_mutation_results + visit_collection(MutationProgressResult, object.mutation_results) + end + end # SubjectProgress + end # Printer + end # CLI + end # Reporter +end # Mutant diff --git a/lib/mutant/reporter/cli/printer/subject_result.rb b/lib/mutant/reporter/cli/printer/subject_result.rb new file mode 100644 index 00000000..fcaf1317 --- /dev/null +++ b/lib/mutant/reporter/cli/printer/subject_result.rb @@ -0,0 +1,28 @@ +module Mutant + class Reporter + class CLI + class Printer + # Subject result printer + class SubjectResult < self + + delegate :subject, :alive_mutation_results, :tests + + # Run report printer + # + # @return [undefined] + # + # @api private + # + def run + status(subject.identification) + tests.each do |test| + puts("- #{test.identification}") + end + visit_collection(MutationResult, alive_mutation_results) + end + + end # SubjectResult + end # Printer + end # CLI + end # Reporter +end # Mutant diff --git a/lib/mutant/reporter/cli/printer/test_result.rb b/lib/mutant/reporter/cli/printer/test_result.rb new file mode 100644 index 00000000..19024e9d --- /dev/null +++ b/lib/mutant/reporter/cli/printer/test_result.rb @@ -0,0 +1,33 @@ +module Mutant + class Reporter + class CLI + class Printer + # Test result reporter + class TestResult < self + + delegate :tests, :runtime + + STATUS_FORMAT = '- %d @ runtime: %s'.freeze + OUTPUT_HEADER = 'Test Output:'.freeze + TEST_FORMAT = ' - %s'.freeze + + # Run test result reporter + # + # @return [undefined] + # + # @api private + # + def run + info(STATUS_FORMAT, tests.length, runtime) + tests.each do |test| + info(TEST_FORMAT, test.identification) + end + puts(OUTPUT_HEADER) + puts(object.output) + end + + end # TestResult + end # Printer + end # CLI + end # Reporter +end # Mutant diff --git a/spec/support/shared_context.rb b/spec/support/shared_context.rb index 5ce1cf23..d1f5c9dc 100644 --- a/spec/support/shared_context.rb +++ b/spec/support/shared_context.rb @@ -14,6 +14,14 @@ module SharedContext end end + def it_reports(expected_content) + it 'writes expected report to output' do + described_class.call(output, reportable) + output.rewind + expect(output.read).to eql(strip_indent(expected_content)) + end + end + # rubocop:disable MethodLength # rubocop:disable AbcSize def setup_shared_context @@ -24,6 +32,7 @@ module SharedContext let(:job_b_result) { Mutant::Runner::JobResult.new(job: job_b, result: mutation_b_result) } let(:test_a) { double('test a', identification: 'test-a') } let(:test_b) { double('test b', identification: 'test-b') } + let(:output) { StringIO.new } let(:matchable_scopes) { double('matchable scopes', length: 10) } let(:message_sequence) { FakeActor::MessageSequence.new } let(:mutations) { [mutation_a, mutation_b] } @@ -31,10 +40,7 @@ module SharedContext let(:mutation_b_node) { s(:nil) } let(:mutation_b) { Mutant::Mutation::Evil.new(subject_a, mutation_b_node) } let(:mutation_a) { Mutant::Mutation::Evil.new(subject_a, mutation_a_node) } - - before do - allow(subject_a).to receive(:mutations).and_return([mutation_a, mutation_b]) - end + let(:subject_a_node) { s(:true) } let(:status) do Mutant::Parallel::Status.new( @@ -54,13 +60,17 @@ module SharedContext let(:subject_a) do double( 'subject a', - node: s(:true), - source: 'true', + node: subject_a_node, + source: Unparser.unparse(subject_a_node), tests: [test_a], identification: 'subject-a' ) end + before do + allow(subject_a).to receive(:mutations).and_return([mutation_a, mutation_b]) + end + let(:env_result) do Mutant::Result::Env.new( env: env, diff --git a/spec/unit/mutant/reporter/cli/printer/config_spec.rb b/spec/unit/mutant/reporter/cli/printer/config_spec.rb new file mode 100644 index 00000000..e378e73c --- /dev/null +++ b/spec/unit/mutant/reporter/cli/printer/config_spec.rb @@ -0,0 +1,33 @@ +RSpec.describe Mutant::Reporter::CLI::Printer::Config do + setup_shared_context + + let(:reportable) { config } + + describe '.call' do + context 'on default config' do + it_reports(<<-REPORT) + Mutant configuration: + Matcher: # + Integration: null + Expect Coverage: 100.00% + Jobs: 1 + Includes: [] + Requires: [] + REPORT + end + + context 'with non default coverage expectation' do + update(:config) { { expected_coverage: 0.1r } } + + it_reports(<<-REPORT) + Mutant configuration: + Matcher: # + Integration: null + Expect Coverage: 10.00% + Jobs: 1 + Includes: [] + Requires: [] + REPORT + end + end +end diff --git a/spec/unit/mutant/reporter/cli/printer/env_progress_spec.rb b/spec/unit/mutant/reporter/cli/printer/env_progress_spec.rb new file mode 100644 index 00000000..e50e00f2 --- /dev/null +++ b/spec/unit/mutant/reporter/cli/printer/env_progress_spec.rb @@ -0,0 +1,76 @@ +RSpec.describe Mutant::Reporter::CLI::Printer::EnvProgress do + setup_shared_context + + update(:config) { { expected_coverage: 0.1r } } + + let(:reportable) { env_result } + + describe '.call' do + context 'without progress' do + update(:subject_a_result) { { mutation_results: [] } } + + it_reports <<-'STR' + Mutant configuration: + Matcher: # + Integration: null + Expect Coverage: 10.00% + Jobs: 1 + Includes: [] + Requires: [] + Subjects: 1 + Mutations: 2 + Kills: 0 + Alive: 0 + Runtime: 4.00s + Killtime: 0.00s + Overhead: Inf% + Coverage: 0.00% + Expected: 10.00% + STR + end + + context 'on full coverage' do + it_reports <<-'STR' + Mutant configuration: + Matcher: # + Integration: null + Expect Coverage: 10.00% + Jobs: 1 + Includes: [] + Requires: [] + Subjects: 1 + Mutations: 2 + Kills: 2 + Alive: 0 + Runtime: 4.00s + Killtime: 2.00s + Overhead: 100.00% + Coverage: 100.00% + Expected: 10.00% + STR + end + + context 'on partial coverage' do + update(:mutation_a_test_result) { { passed: true } } + + it_reports <<-'STR' + Mutant configuration: + Matcher: # + Integration: null + Expect Coverage: 10.00% + Jobs: 1 + Includes: [] + Requires: [] + Subjects: 1 + Mutations: 2 + Kills: 1 + Alive: 1 + Runtime: 4.00s + Killtime: 2.00s + Overhead: 100.00% + Coverage: 50.00% + Expected: 10.00% + STR + end + end +end diff --git a/spec/unit/mutant/reporter/cli/printer/env_result_spec.rb b/spec/unit/mutant/reporter/cli/printer/env_result_spec.rb new file mode 100644 index 00000000..46b9db4a --- /dev/null +++ b/spec/unit/mutant/reporter/cli/printer/env_result_spec.rb @@ -0,0 +1,35 @@ +RSpec.describe Mutant::Reporter::CLI::Printer::EnvResult do + setup_shared_context + + update(:mutation_a_test_result) { { passed: true } } + + let(:reportable) { env_result } + + describe '.call' do + it_reports <<-'STR' + subject-a + - test-a + evil:subject-a:d27d2 + @@ -1,2 +1,2 @@ + -true + +false + ----------------------- + Mutant configuration: + Matcher: # + Integration: null + Expect Coverage: 100.00% + Jobs: 1 + Includes: [] + Requires: [] + Subjects: 1 + Mutations: 2 + Kills: 1 + Alive: 1 + Runtime: 4.00s + Killtime: 2.00s + Overhead: 100.00% + Coverage: 50.00% + Expected: 100.00% + STR + end +end diff --git a/spec/unit/mutant/reporter/cli/printer/mutation_progress_result_spec.rb b/spec/unit/mutant/reporter/cli/printer/mutation_progress_result_spec.rb new file mode 100644 index 00000000..e1f442b4 --- /dev/null +++ b/spec/unit/mutant/reporter/cli/printer/mutation_progress_result_spec.rb @@ -0,0 +1,23 @@ +RSpec.describe Mutant::Reporter::CLI::Printer::MutationProgressResult do + setup_shared_context + + let(:reportable) { mutation_a_result } + + before do + allow(output).to receive(:tty?).and_return(true) + end + + describe '.run' do + context 'on killed mutant' do + update(:mutation_a_test_result) { { passed: true } } + + it_reports Mutant::Color::RED.format('F') + end + + context 'on alive mutant' do + update(:mutation_a_test_result) { { passed: false } } + + it_reports Mutant::Color::GREEN.format('.') + end + end +end diff --git a/spec/unit/mutant/reporter/cli/printer/mutation_result_spec.rb b/spec/unit/mutant/reporter/cli/printer/mutation_result_spec.rb new file mode 100644 index 00000000..17f73a00 --- /dev/null +++ b/spec/unit/mutant/reporter/cli/printer/mutation_result_spec.rb @@ -0,0 +1,110 @@ +RSpec.describe Mutant::Reporter::CLI::Printer::MutationResult do + setup_shared_context + + let(:reportable) { mutation_a_result } + + describe '.call' do + context 'failed kill' do + update(:mutation_a_test_result) { { passed: true } } + + context 'on evil mutation' do + context 'with a diff' do + context 'on a tty' do + before do + allow(output).to receive(:tty?).and_return(true) + end + + it_reports( + [ + [Mutant::Color::NONE, "evil:subject-a:d27d2\n"], + [Mutant::Color::NONE, "@@ -1,2 +1,2 @@\n"], + [Mutant::Color::RED, "-true\n"], + [Mutant::Color::GREEN, "+false\n"], + [Mutant::Color::NONE, "-----------------------\n"] + ].map { |color, text| color.format(text) }.join + ) + end + + context 'on non tty' do + it_reports(<<-'STR') + evil:subject-a:d27d2 + @@ -1,2 +1,2 @@ + -true + +false + ----------------------- + STR + end + end + + context 'without a diff' do + # This is intentionally invalid AST mutant might produce + let(:subject_a_node) { s(:lvar, :super) } + + # Unparses exactly the same way as above node + let(:mutation_a_node) { s(:zsuper) } + + it_reports(<<-REPORT) + evil:subject-a:a5bc7 + --- Internal failure --- + BUG: Mutation NOT resulted in exactly one diff hunk. Please report a reproduction! + Original unparsed source: + super + Original AST: + (lvar :super) + Mutated unparsed source: + super + Mutated AST: + (zsuper) + ----------------------- + REPORT + end + end + + context 'on neutral mutation' do + update(:mutation_a_test_result) { { passed: false } } + + let(:mutation_a) do + Mutant::Mutation::Neutral.new(subject_a, s(:true)) + end + + it_reports(<<-REPORT) + neutral:subject-a:d5318 + --- Neutral failure --- + Original code was inserted unmutated. And the test did NOT PASS. + Your tests do not pass initially or you found a bug in mutant / unparser. + Subject AST: + (true) + Unparsed Source: + true + Test Result: + - 1 @ runtime: 1.0 + - test-a + Test Output: + mutation a test result output + ----------------------- + REPORT + end + + context 'on noop mutation' do + update(:mutation_a_test_result) { { passed: false } } + + let(:mutation_a) do + Mutant::Mutation::Noop.new(subject_a, s(:true)) + end + + it_reports(<<-REPORT) + noop:subject-a:d5318 + ---- Noop failure ----- + No code was inserted. And the test did NOT PASS. + This is typically a problem of your specs not passing unmutated. + Test Result: + - 1 @ runtime: 1.0 + - test-a + Test Output: + mutation a test result output + ----------------------- + REPORT + end + end + end +end diff --git a/spec/unit/mutant/reporter/cli/printer/status_progressive_spec.rb b/spec/unit/mutant/reporter/cli/printer/status_progressive_spec.rb new file mode 100644 index 00000000..847d375c --- /dev/null +++ b/spec/unit/mutant/reporter/cli/printer/status_progressive_spec.rb @@ -0,0 +1,51 @@ +RSpec.describe Mutant::Reporter::CLI::Printer::StatusProgressive do + setup_shared_context + + let(:reportable) { status } + + describe '.call' do + context 'with empty scheduler' do + update(:env_result) { { subject_results: [] } } + + it_reports <<-REPORT + (00/02) 0% - killtime: 0.00s runtime: 4.00s overhead: 4.00s + REPORT + + context 'on non default coverage expectation' do + update(:config) { { expected_coverage: 0.1r } } + + it_reports <<-REPORT + (00/02) 0% - killtime: 0.00s runtime: 4.00s overhead: 4.00s + REPORT + end + end + + context 'with scheduler active on one subject' do + context 'without progress' do + update(:status) { { active_jobs: [].to_set } } + + it_reports(<<-REPORT) + (02/02) 100% - killtime: 2.00s runtime: 4.00s overhead: 2.00s + REPORT + end + + context 'with progress' do + update(:status) { { active_jobs: [job_b, job_a].to_set } } + + context 'on failure' do + update(:mutation_a_test_result) { { passed: true } } + + it_reports(<<-REPORT) + (01/02) 50% - killtime: 2.00s runtime: 4.00s overhead: 2.00s + REPORT + end + + context 'on success' do + it_reports(<<-REPORT) + (02/02) 100% - killtime: 2.00s runtime: 4.00s overhead: 2.00s + REPORT + end + end + end + end +end diff --git a/spec/unit/mutant/reporter/cli/printer/status_spec.rb b/spec/unit/mutant/reporter/cli/printer/status_spec.rb new file mode 100644 index 00000000..4651b442 --- /dev/null +++ b/spec/unit/mutant/reporter/cli/printer/status_spec.rb @@ -0,0 +1,145 @@ +RSpec.describe Mutant::Reporter::CLI::Printer::Status do + setup_shared_context + + let(:reportable) { status } + + describe '.call' do + context 'with empty scheduler' do + update(:env_result) { { subject_results: [] } } + + it_reports <<-REPORT + Mutant configuration: + Matcher: # + Integration: null + Expect Coverage: 100.00% + Jobs: 1 + Includes: [] + Requires: [] + Subjects: 1 + Mutations: 2 + Kills: 0 + Alive: 0 + Runtime: 4.00s + Killtime: 0.00s + Overhead: Inf% + Coverage: 0.00% + Expected: 100.00% + Active subjects: 0 + REPORT + + context 'on non default coverage expectation' do + update(:config) { { expected_coverage: 0.1r } } + + it_reports <<-REPORT + Mutant configuration: + Matcher: # + Integration: null + Expect Coverage: 10.00% + Jobs: 1 + Includes: [] + Requires: [] + Subjects: 1 + Mutations: 2 + Kills: 0 + Alive: 0 + Runtime: 4.00s + Killtime: 0.00s + Overhead: Inf% + Coverage: 0.00% + Expected: 10.00% + Active subjects: 0 + REPORT + end + end + + context 'with scheduler active on one subject' do + context 'without progress' do + update(:status) { { active_jobs: [].to_set } } + + it_reports(<<-REPORT) + Mutant configuration: + Matcher: # + Integration: null + Expect Coverage: 100.00% + Jobs: 1 + Includes: [] + Requires: [] + Subjects: 1 + Mutations: 2 + Kills: 2 + Alive: 0 + Runtime: 4.00s + Killtime: 2.00s + Overhead: 100.00% + Coverage: 100.00% + Expected: 100.00% + Active subjects: 0 + REPORT + end + + context 'with progress' do + update(:status) { { active_jobs: [job_b, job_a].to_set } } + + context 'on failure' do + update(:mutation_a_test_result) { { passed: true } } + + it_reports(<<-REPORT) + Mutant configuration: + Matcher: # + Integration: null + Expect Coverage: 100.00% + Jobs: 1 + Includes: [] + Requires: [] + Subjects: 1 + Mutations: 2 + Kills: 1 + Alive: 1 + Runtime: 4.00s + Killtime: 2.00s + Overhead: 100.00% + Coverage: 50.00% + Expected: 100.00% + Active Jobs: + 0: evil:subject-a:d27d2 + 1: evil:subject-a:d5a9d + Active subjects: 1 + subject-a mutations: 2 + F. + (01/02) 50% - killtime: 2.00s runtime: 2.00s overhead: 0.00s + - test-a + REPORT + end + + context 'on success' do + it_reports(<<-REPORT) + Mutant configuration: + Matcher: # + Integration: null + Expect Coverage: 100.00% + Jobs: 1 + Includes: [] + Requires: [] + Subjects: 1 + Mutations: 2 + Kills: 2 + Alive: 0 + Runtime: 4.00s + Killtime: 2.00s + Overhead: 100.00% + Coverage: 100.00% + Expected: 100.00% + Active Jobs: + 0: evil:subject-a:d27d2 + 1: evil:subject-a:d5a9d + Active subjects: 1 + subject-a mutations: 2 + .. + (02/02) 100% - killtime: 2.00s runtime: 2.00s overhead: 0.00s + - test-a + REPORT + end + end + end + end +end diff --git a/spec/unit/mutant/reporter/cli/printer/subject_progress_spec.rb b/spec/unit/mutant/reporter/cli/printer/subject_progress_spec.rb new file mode 100644 index 00000000..3734b20a --- /dev/null +++ b/spec/unit/mutant/reporter/cli/printer/subject_progress_spec.rb @@ -0,0 +1,37 @@ +RSpec.describe Mutant::Reporter::CLI::Printer::SubjectProgress do + setup_shared_context + + let(:reportable) { subject_a_result } + + describe '.call' do + context 'on full coverage' do + it_reports <<-'STR' + subject-a mutations: 2 + .. + (02/02) 100% - killtime: 2.00s runtime: 2.00s overhead: 0.00s + - test-a + STR + end + + context 'on partial coverage' do + update(:mutation_a_test_result) { { passed: true } } + + it_reports <<-'STR' + subject-a mutations: 2 + F. + (01/02) 50% - killtime: 2.00s runtime: 2.00s overhead: 0.00s + - test-a + STR + end + + context 'without results' do + update(:subject_a_result) { { mutation_results: [] } } + + it_reports <<-'STR' + subject-a mutations: 2 + (00/02) 0% - killtime: 0.00s runtime: 0.00s overhead: 0.00s + - test-a + STR + end + end +end diff --git a/spec/unit/mutant/reporter/cli/printer/subject_result_spec.rb b/spec/unit/mutant/reporter/cli/printer/subject_result_spec.rb new file mode 100644 index 00000000..54e90c4c --- /dev/null +++ b/spec/unit/mutant/reporter/cli/printer/subject_result_spec.rb @@ -0,0 +1,37 @@ +RSpec.describe Mutant::Reporter::CLI::Printer::SubjectResult do + setup_shared_context + + let(:reportable) { subject_a_result } + + describe '.call' do + context 'on full coverage' do + it_reports <<-'STR' + subject-a + - test-a + STR + end + + context 'on partial coverage' do + update(:mutation_a_test_result) { { passed: true } } + + it_reports <<-'STR' + subject-a + - test-a + evil:subject-a:d27d2 + @@ -1,2 +1,2 @@ + -true + +false + ----------------------- + STR + end + + context 'without results' do + update(:subject_a_result) { { mutation_results: [] } } + + it_reports <<-'STR' + subject-a + - test-a + STR + end + end +end diff --git a/spec/unit/mutant/reporter/cli/printer/test_result_spec.rb b/spec/unit/mutant/reporter/cli/printer/test_result_spec.rb new file mode 100644 index 00000000..10b2a25c --- /dev/null +++ b/spec/unit/mutant/reporter/cli/printer/test_result_spec.rb @@ -0,0 +1,14 @@ +RSpec.describe Mutant::Reporter::CLI::Printer::TestResult do + setup_shared_context + + let(:reportable) { mutation_a_test_result } + + describe '.call' do + it_reports <<-'STR' + - 1 @ runtime: 1.0 + - test-a + Test Output: + mutation a test result output + STR + end +end diff --git a/spec/unit/mutant/reporter/cli/printer_spec.rb b/spec/unit/mutant/reporter/cli/printer_spec.rb new file mode 100644 index 00000000..176f9afe --- /dev/null +++ b/spec/unit/mutant/reporter/cli/printer_spec.rb @@ -0,0 +1,140 @@ +RSpec.describe Mutant::Reporter::CLI::Printer do + let(:output) { StringIO.new } + + subject { class_under_test.call(output, reportable) } + + def self.it_reports(expectation) + it 'writes expected report' do + allow(output).to receive(:tty?).and_return(tty?) + subject + output.rewind + expect(output.read).to eql(strip_indent(expectation)) + end + end + + let(:reportable) { double('Reportable', success?: success?) } + let(:tty?) { true } + let(:success?) { true } + + context '.call' do + let(:class_under_test) do + Class.new(described_class) do + def run + puts object + end + end + end + + let(:reportable) { 'foo' } + + it_reports "foo\n" + end + + context '#status' do + let(:class_under_test) do + Class.new(described_class) do + def run + status('foo %s', 'bar') + end + end + end + + context 'on tty' do + context 'on success' do + it_reports Mutant::Color::GREEN.format('foo bar') << "\n" + end + + context 'on failure' do + let(:success?) { false } + it_reports Mutant::Color::RED.format('foo bar') << "\n" + end + end + + context 'on no tty' do + let(:tty?) { false } + + context 'on success' do + it_reports "foo bar\n" + end + + context 'on failure' do + let(:success?) { false } + + it_reports "foo bar\n" + end + end + end + + context '#visit_collection' do + let(:class_under_test) do + reporter = nested_reporter + Class.new(described_class) do + define_method(:run) do + visit_collection(reporter, %w[foo bar]) + end + end + end + + let(:nested_reporter) do + Class.new(described_class) do + def run + puts object + end + end + end + + it_reports "foo\nbar\n" + end + + context '#visit' do + let(:class_under_test) do + reporter = nested_reporter + Class.new(described_class) do + define_method(:run) do + visit(reporter, 'foo') + end + end + end + + let(:nested_reporter) do + Class.new(described_class) do + def run + puts object + end + end + end + + it_reports "foo\n" + end + + context '#info' do + let(:class_under_test) do + Class.new(described_class) do + def run + info('%s - %s', 'foo', 'bar') + end + end + end + + it_reports "foo - bar\n" + end + + context '#colorize' do + let(:class_under_test) do + Class.new(described_class) do + def run + puts(colorize(Mutant::Color::RED, 'foo')) + end + end + end + + context 'when output is a tty?' do + it_reports Mutant::Color::RED.format('foo') << "\n" + end + + context 'when output is NOT a tty?' do + let(:tty?) { false } + it_reports "foo\n" + end + end +end diff --git a/spec/unit/mutant/reporter/cli_spec.rb b/spec/unit/mutant/reporter/cli_spec.rb index 0f2f64d9..c56e337b 100644 --- a/spec/unit/mutant/reporter/cli_spec.rb +++ b/spec/unit/mutant/reporter/cli_spec.rb @@ -19,8 +19,10 @@ RSpec.describe Mutant::Reporter::CLI do ) end + let(:tty?) { false } + let(:progressive_format) do - described_class::Format::Progressive.new(tty: false) + described_class::Format::Progressive.new(tty: tty?) end let(:format) { framed_format } @@ -128,21 +130,85 @@ RSpec.describe Mutant::Reporter::CLI do REPORT end + context 'with non default coverage expectation' do + let(:format) { progressive_format } + update(:config) { { expected_coverage: 0.1r } } + + it_reports(<<-REPORT) + Mutant configuration: + Matcher: # + Integration: null + Expect Coverage: 10.00% + Jobs: 1 + Includes: [] + Requires: [] + REPORT + end + context 'on framed format' do it_reports '[tput-prepare]' end end + describe '#report' do + subject { object.report(env_result) } + + it_reports(<<-REPORT) + Mutant configuration: + Matcher: # + Integration: null + Expect Coverage: 100.00% + Jobs: 1 + Includes: [] + Requires: [] + Subjects: 1 + Mutations: 2 + Kills: 2 + Alive: 0 + Runtime: 4.00s + Killtime: 2.00s + Overhead: 100.00% + Coverage: 100.00% + Expected: 100.00% + REPORT + end + describe '#progress' do subject { object.progress(status) } + context 'on framed format' do + let(:format) { framed_format } + + it_reports(<<-REPORT) + [tput-restore]Mutant configuration: + Matcher: # + Integration: null + Expect Coverage: 100.00% + Jobs: 1 + Includes: [] + Requires: [] + Subjects: 1 + Mutations: 2 + Kills: 2 + Alive: 0 + Runtime: 4.00s + Killtime: 2.00s + Overhead: 100.00% + Coverage: 100.00% + Expected: 100.00% + Active subjects: 0 + REPORT + end + context 'on progressive format' do let(:format) { progressive_format } context 'with empty scheduler' do update(:env_result) { { subject_results: [] } } - it_reports "(00/02) 0% - killtime: 0.00s runtime: 4.00s overhead: 4.00s\n" + let(:tty?) { true } + + it_reports Mutant::Color::RED.format('(00/02) 0% - killtime: 0.00s runtime: 4.00s overhead: 4.00s') << "\n" end context 'with last mutation present' do @@ -159,315 +225,5 @@ RSpec.describe Mutant::Reporter::CLI do end end - context 'on framed format' do - context 'with empty scheduler' do - update(:env_result) { { subject_results: [] } } - - it_reports <<-REPORT - [tput-restore]Mutant configuration: - Matcher: # - Integration: null - Expect Coverage: 100.00% - Jobs: 1 - Includes: [] - Requires: [] - Subjects: 1 - Mutations: 2 - Kills: 0 - Alive: 0 - Runtime: 4.00s - Killtime: 0.00s - Overhead: Inf% - Coverage: 0.00% - Expected: 100.00% - Active subjects: 0 - REPORT - end - - context 'with scheduler active on one subject' do - context 'without progress' do - update(:status) { { active_jobs: [].to_set } } - - it_reports(<<-REPORT) - [tput-restore]Mutant configuration: - Matcher: # - Integration: null - Expect Coverage: 100.00% - Jobs: 1 - Includes: [] - Requires: [] - Subjects: 1 - Mutations: 2 - Kills: 2 - Alive: 0 - Runtime: 4.00s - Killtime: 2.00s - Overhead: 100.00% - Coverage: 100.00% - Expected: 100.00% - Active subjects: 0 - REPORT - end - - context 'with progress' do - update(:status) { { active_jobs: [job_a].to_set } } - - context 'on failure' do - update(:mutation_a_test_result) { { passed: true } } - - it_reports(<<-REPORT) - [tput-restore]Mutant configuration: - Matcher: # - Integration: null - Expect Coverage: 100.00% - Jobs: 1 - Includes: [] - Requires: [] - Subjects: 1 - Mutations: 2 - Kills: 1 - Alive: 1 - Runtime: 4.00s - Killtime: 2.00s - Overhead: 100.00% - Coverage: 50.00% - Expected: 100.00% - Active Jobs: - 0: evil:subject-a:d27d2 - Active subjects: 1 - subject-a mutations: 2 - F. - (01/02) 50% - killtime: 2.00s runtime: 2.00s overhead: 0.00s - - test-a - REPORT - end - - context 'on success' do - it_reports(<<-REPORT) - [tput-restore]Mutant configuration: - Matcher: # - Integration: null - Expect Coverage: 100.00% - Jobs: 1 - Includes: [] - Requires: [] - Subjects: 1 - Mutations: 2 - Kills: 2 - Alive: 0 - Runtime: 4.00s - Killtime: 2.00s - Overhead: 100.00% - Coverage: 100.00% - Expected: 100.00% - Active Jobs: - 0: evil:subject-a:d27d2 - Active subjects: 1 - subject-a mutations: 2 - .. - (02/02) 100% - killtime: 2.00s runtime: 2.00s overhead: 0.00s - - test-a - REPORT - end - end - end - end - - describe '#report' do - subject { object.report(status.payload) } - - context 'with full coverage' do - it_reports(<<-REPORT) - Mutant configuration: - Matcher: # - Integration: null - Expect Coverage: 100.00% - Jobs: 1 - Includes: [] - Requires: [] - Subjects: 1 - Mutations: 2 - Kills: 2 - Alive: 0 - Runtime: 4.00s - Killtime: 2.00s - Overhead: 100.00% - Coverage: 100.00% - Expected: 100.00% - REPORT - end - - context 'and partial coverage' do - update(:mutation_a_test_result) { { passed: true } } - - context 'on evil mutation' do - context 'with a diff' do - it_reports(<<-REPORT) - subject-a - - test-a - evil:subject-a:d27d2 - @@ -1,2 +1,2 @@ - -true - +false - ----------------------- - Mutant configuration: - Matcher: # - Integration: null - Expect Coverage: 100.00% - Jobs: 1 - Includes: [] - Requires: [] - Subjects: 1 - Mutations: 2 - Kills: 1 - Alive: 1 - Runtime: 4.00s - Killtime: 2.00s - Overhead: 100.00% - Coverage: 50.00% - Expected: 100.00% - REPORT - end - - context 'without a diff' do - let(:mutation_a_node) { s(:true) } - - it_reports(<<-REPORT) - subject-a - - test-a - evil:subject-a:d5318 - Original source: - true - Mutated Source: - true - BUG: Mutation NOT resulted in exactly one diff hunk. Please report a reproduction! - ----------------------- - Mutant configuration: - Matcher: # - Integration: null - Expect Coverage: 100.00% - Jobs: 1 - Includes: [] - Requires: [] - Subjects: 1 - Mutations: 2 - Kills: 1 - Alive: 1 - Runtime: 4.00s - Killtime: 2.00s - Overhead: 100.00% - Coverage: 50.00% - Expected: 100.00% - REPORT - end - end - - context 'on neutral mutation' do - update(:mutation_a_test_result) { { passed: false } } - - let(:mutation_a) do - Mutant::Mutation::Neutral.new(subject_a, s(:true)) - end - - it_reports(<<-REPORT) - subject-a - - test-a - neutral:subject-a:d5318 - --- Neutral failure --- - Original code was inserted unmutated. And the test did NOT PASS. - Your tests do not pass initially or you found a bug in mutant / unparser. - Subject AST: - (true) - Unparsed Source: - true - Test Result: - - 1 @ runtime: 1.0 - - test-a - Test Output: - mutation a test result output - ----------------------- - neutral:subject-a:d5318 - --- Neutral failure --- - Original code was inserted unmutated. And the test did NOT PASS. - Your tests do not pass initially or you found a bug in mutant / unparser. - Subject AST: - (true) - Unparsed Source: - true - Test Result: - - 1 @ runtime: 1.0 - - test-a - Test Output: - mutation b test result output - ----------------------- - Mutant configuration: - Matcher: # - Integration: null - Expect Coverage: 100.00% - Jobs: 1 - Includes: [] - Requires: [] - Subjects: 1 - Mutations: 2 - Kills: 0 - Alive: 2 - Runtime: 4.00s - Killtime: 2.00s - Overhead: 100.00% - Coverage: 0.00% - Expected: 100.00% - REPORT - end - - context 'on noop mutation' do - update(:mutation_a_test_result) { { passed: false } } - - let(:mutation_a) do - Mutant::Mutation::Noop.new(subject_a, s(:true)) - end - - it_reports(<<-REPORT) - subject-a - - test-a - noop:subject-a:d5318 - ---- Noop failure ----- - No code was inserted. And the test did NOT PASS. - This is typically a problem of your specs not passing unmutated. - Test Result: - - 1 @ runtime: 1.0 - - test-a - Test Output: - mutation a test result output - ----------------------- - noop:subject-a:d5318 - ---- Noop failure ----- - No code was inserted. And the test did NOT PASS. - This is typically a problem of your specs not passing unmutated. - Test Result: - - 1 @ runtime: 1.0 - - test-a - Test Output: - mutation b test result output - ----------------------- - Mutant configuration: - Matcher: # - Integration: null - Expect Coverage: 100.00% - Jobs: 1 - Includes: [] - Requires: [] - Subjects: 1 - Mutations: 2 - Kills: 0 - Alive: 2 - Runtime: 4.00s - Killtime: 2.00s - Overhead: 100.00% - Coverage: 0.00% - Expected: 100.00% - REPORT - end - end - end end end diff --git a/spec/unit/mutant/reporter/trace_spec.rb b/spec/unit/mutant/reporter/trace_spec.rb index 78a0f0b0..655cf011 100644 --- a/spec/unit/mutant/reporter/trace_spec.rb +++ b/spec/unit/mutant/reporter/trace_spec.rb @@ -6,4 +6,16 @@ RSpec.describe Mutant::Reporter::Trace do it { should eql(0.0) } end + + let(:reportable) { double('Reportable') } + + %i[report start progress].each do |name| + describe "##{name}" do + subject { object.public_send(name, reportable) } + + it 'logs the reportable' do + expect { subject }.to change { object.public_send("#{name}_calls") }.from([]).to([reportable]) + end + end + end end