m.rb |
|
|---|---|
|
Rush at the Bristol Colston Hall May 1979 InstallInstall via:
If you’re using Bundler, you’ll need to include it in your Gemfile. Toss it into the
Developing a RubyGem? Add
UsageBasically, I was sick of using the Given this file:
You can run a test by line number, using format
Hit the wrong line number? No problem,
Want to run the whole test? Just leave off the line number.
Supports
LicenseThis gem is MIT licensed, please see |
|
M, your metal test runnerMaybe this gem should have a longer name? Metal? |
module M
VERSION = "1.3.1" unless defined?(VERSION) |
|
Accept arguments coming from bin/m and run tests, then bail out immediately. |
def self.run(argv)
exit Runner.new(argv).run
end |
Runner is in charge of running your tests.Instead of slamming all of this junk in an |
class Runner
def initialize(argv)
@argv = argv
end |
|
There’s two steps to running our tests: 1. Parsing the given input for the tests we need to find (or groups of tests) 2. Run those tests we found that match what you wanted |
def run
parse
execute
end
private
def parse |
|
With no arguments, |
if @argv.empty? |
|
Just shell out to |
exec "rake test"
else
parse_options! @argv |
|
Parse out ARGV, it should be coming in in a format like |
@file, line = @argv.first.split(':')
@line ||= line.to_i |
|
If this file is a directory, not a file, run the tests inside of this directory |
if Dir.exist?(@file) |
|
Make a new rake test task with a hopefully unique name, and run every test looking file in it |
require "rake/testtask"
Rake::TestTask.new(:m_custom) do |t|
t.libs << 'test'
t.pattern = "#{@file}/*test*.rb"
end |
|
Invoke the rake task and exit, hopefully it’ll work! |
Rake::Task['m_custom'].invoke
exit
end
end
end
def parse_options!(argv)
require 'optparse'
OptionParser.new do |opts|
opts.banner = 'Options:'
opts.version = M::VERSION
opts.on '-h', '--help', 'Display this help.' do
puts "Usage: m [OPTIONS] [FILES]\n\n", opts
exit
end
opts.on '--version', 'Display the version.' do
puts "m #{M::VERSION}"
exit
end
opts.on '-l', '--line LINE', Integer, 'Line number for file.' do |line|
@line = line
end
opts.parse! argv
end
end
def execute |
|
Locate tests to run that may be inside of this line. There could be more than one! |
tests_to_run = tests.within(@line) |
|
If we found any tests, |
if tests_to_run.size > 0 |
|
assemble the regexp to run these tests, |
test_names = tests_to_run.map(&:name).join('|') |
|
set up the args needed for the runner |
test_arguments = ["-n", "/(#{test_names})/"] |
|
directly run the tests from here and exit with the status of the tests passing or failing |
if defined?(MiniTest)
MiniTest::Unit.runner.run test_arguments
elsif defined?(Test)
Test::Unit::AutoRunner.run(false, nil, test_arguments)
else
not_supported
end
elsif tests.size > 0 |
|
Otherwise we found no tests on this line, so you need to pick one. |
message = "No tests found on line #{@line}. Valid tests to run:\n\n" |
|
For every test ordered by line number, spit out the test name and line number where it starts, |
tests.by_line_number do |test|
message << "#{sprintf("%0#{tests.column_size}s", test.name)}: m #{@file}:#{test.start_line}\n"
end |
|
Spit out helpful message and bail |
STDERR.puts message
false
end
end |
|
Finds all test suites in this test file, with test methods included. |
def suites |
|
Since we’re not using |
$:.unshift "./test", "./lib"
begin |
|
Fire up this Ruby file. Let’s hope it actually has tests. |
load @file
rescue LoadError => e |
|
Fail with a happier error message instead of spitting out a backtrace from this gem |
STDERR.puts "Failed loading test file:\n#{e.message}"
return []
end |
|
Figure out what test framework we’re using |
if defined?(MiniTest)
suites = MiniTest::Unit::TestCase.test_suites
elsif defined?(Test)
suites = Test::Unit::TestCase.test_suites
else
suites = []
not_supported
end |
|
Use some janky internal APIs to group test methods by test suite. |
suites.inject({}) do |suites, suite_class| |
|
End up with a hash of suite class name to an array of test methods, so we can later find them and ignore empty test suites |
suites[suite_class] = suite_class.test_methods if suite_class.test_methods.size > 0
suites
end
end |
|
Shoves tests together in our custom container and collection classes. Memoize it since it’s unnecessary to do this more than one for a given file. |
def tests
@tests ||= begin
require "m/test_collection"
require "m/test_method" |
|
With each suite and array of tests, and with each test method present in this test file, shove a new test method into this collection. |
suites.inject(TestCollection.new) do |collection, (suite_class, test_methods)|
test_methods.each do |test_method|
collection << TestMethod.create(suite_class, test_method)
end
collection
end
end
end |
|
Fail loudly if this isn’t supported |
def not_supported
STDERR.puts "This test framework is not supported! Please open up an issue at https://github.com/qrush/m !"
false
end
end
end |