-
-
Notifications
You must be signed in to change notification settings - Fork 152
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 test minitest integration #445
Merged
Merged
Changes from 1 commit
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next
Next commit
Add test minitest integration
[fix #92]
- Loading branch information
commit 9ceb2bd6506072855fe0e3db27d489da1bffb7d6
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,3 @@ | ||
--- | ||
threshold: 16 | ||
total_score: 1364 | ||
total_score: 1395 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,174 @@ | ||
# frozen_string_literal: true | ||
|
||
require 'minitest' | ||
require 'mutant/minitest/coverage' | ||
|
||
module Minitest | ||
# Prevent autorun from running tests when the VM closes | ||
# | ||
# Mutant needs control about the exit status of the VM and | ||
# the moment of test execution | ||
# | ||
# @api private | ||
# | ||
# @return [nil] | ||
def self.autorun; end | ||
end # Minitest | ||
|
||
module Mutant | ||
class Integration | ||
# Minitest integration | ||
class Minitest < self | ||
TEST_FILE_PATTERN = './test/**/{test_*,*_test}.rb' | ||
IDENTIFICATION_FORMAT = 'minitest:%s#%s' | ||
|
||
private_constant(*constants(false)) | ||
|
||
# Compose a runnable with test method | ||
# | ||
# This looks actually like a missing object on minitest implementation. | ||
class TestCase | ||
include Adamantium, Concord.new(:klass, :test_method) | ||
|
||
# Identification string | ||
# | ||
# @return [String] | ||
def identification | ||
IDENTIFICATION_FORMAT % [klass, test_method] | ||
end | ||
memoize :identification | ||
|
||
# Run test case | ||
# | ||
# @param [Object] reporter | ||
# | ||
# @return [Boolean] | ||
def call(reporter) | ||
::Minitest::Runnable.run_one_method(klass, test_method, reporter) | ||
reporter.passed? | ||
end | ||
|
||
# Cover expression syntaxes | ||
# | ||
# @return [String, nil] | ||
def expression_syntax | ||
klass.resolve_cover_expression | ||
end | ||
|
||
end # TestCase | ||
|
||
private_constant(*constants(false)) | ||
|
||
# Setup integration | ||
# | ||
# @return [self] | ||
def setup | ||
Pathname.glob(TEST_FILE_PATTERN) | ||
.map(&:to_s) | ||
.each(&method(:require)) | ||
|
||
self | ||
end | ||
|
||
# Call test integration | ||
# | ||
# @param [Array<Tests>] tests | ||
# | ||
# @return [Result::Test] | ||
# | ||
# rubocop:disable MethodLength | ||
# | ||
# ignore :reek:TooManyStatements | ||
def call(tests) | ||
test_cases = tests.map(&all_tests_index.method(:fetch)) | ||
output = StringIO.new | ||
start = Time.now | ||
|
||
reporter = ::Minitest::SummaryReporter.new(output) | ||
|
||
reporter.start | ||
|
||
test_cases.each do |test| | ||
break unless test.call(reporter) | ||
end | ||
|
||
output.rewind | ||
|
||
Result::Test.new( | ||
passed: reporter.passed?, | ||
tests: tests, | ||
output: output.read, | ||
runtime: Time.now - start | ||
) | ||
end | ||
|
||
# All tests exposed by this integration | ||
# | ||
# @return [Array<Test>] | ||
def all_tests | ||
all_tests_index.keys | ||
end | ||
memoize :all_tests | ||
|
||
private | ||
|
||
# The index of all tests to runnable test cases | ||
# | ||
# @return [Hash<Test,TestCase>] | ||
def all_tests_index | ||
all_test_cases.each_with_object({}) do |test_case, index| | ||
index[construct_test(test_case)] = test_case | ||
end | ||
end | ||
memoize :all_tests_index | ||
|
||
# Construct test from test case | ||
# | ||
# @param [TestCase] | ||
# | ||
# @return [Test] | ||
def construct_test(test_case) | ||
Test.new( | ||
id: test_case.identification, | ||
expression: config.expression_parser.call(test_case.expression_syntax) | ||
) | ||
end | ||
|
||
# All minitest test cases | ||
# | ||
# Intentional utility method. | ||
# | ||
# @return [Array<TestCase>] | ||
def all_test_cases | ||
::Minitest::Runnable | ||
.runnables | ||
.select(&method(:allow_runnable?)) | ||
.flat_map(&method(:test_case)) | ||
end | ||
|
||
# Test if runnable qualifies for mutation testing | ||
# | ||
# @param [Class] | ||
# | ||
# @return [Bool] | ||
# | ||
# ignore :reek:UtilityFunction | ||
def allow_runnable?(klass) | ||
!klass.equal?(::Minitest::Runnable) && klass.resolve_cover_expression | ||
end | ||
|
||
# Turn a minitest runnable into its test cases | ||
# | ||
# Intentional utility method. | ||
# | ||
# @param [Object] runnable | ||
# | ||
# @return [Array<TestCase>] | ||
# | ||
# ignore :reek:UtilityFunction | ||
def test_case(runnable) | ||
runnable.runnable_methods.map { |method| TestCase.new(runnable, method) } | ||
end | ||
end # Minitest | ||
end # Integration | ||
end # Mutant |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
# frozen_string_literal: true | ||
|
||
require 'minitest' | ||
|
||
module Mutant | ||
module Minitest | ||
module Coverage | ||
# Setup coverage declaration for current class | ||
# | ||
# @param [String] | ||
# | ||
# @example | ||
# | ||
# class MyTest < MiniTest::Test | ||
# cover 'MyCode*' | ||
# | ||
# def test_some_stuff | ||
# end | ||
# end | ||
# | ||
# @api public | ||
def cover(expression) | ||
fail "#{self} already declares to cover: #{@covers}" if @covers | ||
|
||
@cover_expression = expression | ||
end | ||
|
||
# Effective coverage expression | ||
# | ||
# @return [String, nil] | ||
# | ||
# @api private | ||
def resolve_cover_expression | ||
return @cover_expression if defined?(@cover_expression) | ||
|
||
try_superclass_cover_expression | ||
end | ||
|
||
private | ||
|
||
# Attempt to resolve superclass cover expressio | ||
# | ||
# @return [String, nil] | ||
# | ||
# @api private | ||
def try_superclass_cover_expression | ||
return if superclass.equal?(::Minitest::Runnable) | ||
|
||
superclass.resolve_cover_expression | ||
end | ||
|
||
end # Coverage | ||
end # Minitest | ||
end # Mutant | ||
|
||
Minitest::Test.extend(Mutant::Minitest::Coverage) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
# frozen_string_literal: true | ||
|
||
require File.expand_path('lib/mutant/version', __dir__) | ||
|
||
Gem::Specification.new do |gem| | ||
gem.name = 'mutant-minitest' | ||
gem.version = Mutant::VERSION.dup | ||
gem.authors = ['Markus Schirp'] | ||
gem.email = %w[[email protected]] | ||
gem.description = 'Minitest integration for mutant' | ||
gem.summary = gem.description | ||
gem.homepage = 'https://github.com/mbj/mutant' | ||
gem.license = 'MIT' | ||
|
||
gem.require_paths = %w[lib] | ||
gem.files = `git ls-files -- lib/mutant/{minitest,/integration/minitest.rb}`.split("\n") | ||
gem.test_files = `git ls-files -- spec/integration/mutant/minitest.rb`.split("\n") | ||
gem.extra_rdoc_files = %w[LICENSE] | ||
|
||
gem.add_runtime_dependency('minitest', '~> 5.11') | ||
gem.add_runtime_dependency('mutant', "~> #{gem.version}") | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
# frozen_string_literal: true | ||
|
||
RSpec.describe 'minitest integration', mutant: false do | ||
|
||
let(:base_cmd) { 'bundle exec mutant -I test -I lib --require test_app --use minitest' } | ||
|
||
let(:gemfile) { 'Gemfile.minitest' } | ||
|
||
it_behaves_like 'framework integration' | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
source 'https://rubygems.org' | ||
|
||
gem 'minitest', '~> 5.11' | ||
gem 'mutant', path: '../' | ||
gem 'mutant-minitest', path: '../' | ||
gem 'adamantium' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
require 'minitest/autorun' | ||
require 'mutant/minitest/coverage' | ||
|
||
class LiteralTest < Minitest::Test | ||
cover 'TestApp::Literal*' | ||
|
||
def test_command | ||
object = ::TestApp::Literal.new | ||
|
||
assert_equal(object, object.command('x')) | ||
end | ||
|
||
def test_string | ||
assert_equal('string', ::TestApp::Literal.new.string) | ||
end | ||
end |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could rewrite this monkey patch on one line with something like
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting idea to shorten it like this. I wounder how we can apply the axioms to deterministically prefer one form over the other. //cc @dkubb.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm kind of partial to the
def
form myself. I generally prefer to use built-in language syntax over meta-programming methods, assuming the end result is equal.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. And the rule of least power supports this as the metaprogramming closure has a much wider scope. Using the primary syntax has "less power" (scope wise) so should be preferred here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you want to stick with the primary syntax here you could still cut two lines by instead writing
I know this syntax is usually frowned upon but it might make sense in a context like this. It also has the benefit then of not pinning YARD documentation to the toplevel
Minitest
namespace.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reopening the scope
Minitest
is not needed technically, but maybe for consistency?