m 1.3.3 → 1.3.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.travis.yml +8 -4
- data/Gemfile +1 -0
- data/README.md +11 -6
- data/Rakefile +10 -1
- data/benchmarks/20150430-benchmark.log +26 -0
- data/gemfiles/minitest4.gemfile +1 -1
- data/gemfiles/minitest4.gemfile.lock +30 -18
- data/gemfiles/minitest5.gemfile +1 -1
- data/gemfiles/minitest5.gemfile.lock +29 -17
- data/lib/m.rb +1 -271
- data/lib/m/executor.rb +101 -0
- data/lib/m/frameworks.rb +34 -0
- data/lib/m/parser.rb +74 -0
- data/lib/m/runner.rb +20 -0
- data/lib/m/runners/base.rb +17 -0
- data/lib/m/runners/minitest_4.rb +13 -0
- data/lib/m/runners/minitest_5.rb +17 -0
- data/lib/m/runners/test_unit.rb +13 -0
- data/lib/m/runners/unsupported_framework.rb +21 -0
- data/lib/m/testable.rb +15 -0
- data/lib/version.rb +1 -1
- data/m.gemspec +1 -1
- data/test/active_support_test.rb +17 -17
- data/test/bench.rb +25 -8
- data/test/empty_test.rb +1 -1
- data/test/everything_test.rb +11 -6
- data/test/examples/active_support_example_test.rb +0 -1
- data/test/examples/active_support_unescaped_example_test.rb +0 -1
- data/test/examples/empty_example_test.rb +2 -2
- data/test/examples/minitest_4_example_test.rb +4 -6
- data/test/examples/minitest_5_example_test.rb +4 -5
- data/test/examples/multiple_example_test.rb +0 -1
- data/test/examples/subdir/a_test.rb +2 -2
- data/test/examples/subdir/b_test.rb +2 -2
- data/test/examples/subdir/c_test.rb +2 -2
- data/test/examples/subdir_with_failures/a_test.rb +7 -0
- data/test/examples/test_unit_example_test.rb +12 -12
- data/test/minitest_4_test.rb +13 -13
- data/test/minitest_5_test.rb +6 -6
- data/test/multiple_test.rb +3 -3
- data/test/options_test.rb +6 -6
- data/test/test_helper.rb +13 -12
- data/test/test_unit_test.rb +29 -27
- metadata +13 -16
data/lib/m/executor.rb
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
require_relative 'runners/base'
|
2
|
+
require_relative 'runners/minitest_5'
|
3
|
+
require_relative 'runners/minitest_4'
|
4
|
+
require_relative 'runners/test_unit'
|
5
|
+
require_relative 'runners/unsupported_framework'
|
6
|
+
|
7
|
+
module M
|
8
|
+
class Executor
|
9
|
+
def initialize(testable)
|
10
|
+
@testable = testable
|
11
|
+
end
|
12
|
+
|
13
|
+
def execute
|
14
|
+
# Locate tests to run that may be inside of this line. There could be more than one!
|
15
|
+
tests_to_run = tests.within(testable.line)
|
16
|
+
|
17
|
+
# If we found any tests,
|
18
|
+
if tests_to_run.size > 0
|
19
|
+
# assemble the regexp to run these tests,
|
20
|
+
test_names = tests_to_run.map { |test| Regexp.escape(test.name) }.join('|')
|
21
|
+
|
22
|
+
# set up the args needed for the runner
|
23
|
+
test_arguments = ["-n", "/^(#{test_names})$/"]
|
24
|
+
|
25
|
+
# directly run the tests from here and exit with the status of the tests passing or failing
|
26
|
+
runner.run(test_arguments)
|
27
|
+
elsif tests.size > 0
|
28
|
+
# Otherwise we found no tests on this line, so you need to pick one.
|
29
|
+
message = "No tests found on line #{testable.line}. Valid tests to run:\n\n"
|
30
|
+
|
31
|
+
# For every test ordered by line number,
|
32
|
+
# spit out the test name and line number where it starts,
|
33
|
+
tests.by_line_number do |test|
|
34
|
+
message << "#{sprintf("%0#{tests.column_size}s", test.name)}: m #{testable.file}:#{test.start_line}\n"
|
35
|
+
end
|
36
|
+
|
37
|
+
# Spit out helpful message and bail
|
38
|
+
STDERR.puts message
|
39
|
+
false
|
40
|
+
else
|
41
|
+
# There were no tests at all
|
42
|
+
message = "There were no tests found.\n\n"
|
43
|
+
STDERR.puts message
|
44
|
+
false
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
attr_reader :testable
|
51
|
+
|
52
|
+
# Shoves tests together in our custom container and collection classes.
|
53
|
+
# Memoize it since it's unnecessary to do this more than one for a given file.
|
54
|
+
def tests
|
55
|
+
@tests ||= begin
|
56
|
+
require "m/test_collection"
|
57
|
+
require "m/test_method"
|
58
|
+
# With each suite and array of tests,
|
59
|
+
# and with each test method present in this test file,
|
60
|
+
# shove a new test method into this collection.
|
61
|
+
suites.inject(TestCollection.new) do |collection, (suite_class, test_methods)|
|
62
|
+
test_methods.each do |test_method|
|
63
|
+
collection << TestMethod.create(suite_class, test_method)
|
64
|
+
end
|
65
|
+
collection
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
|
71
|
+
# Finds all test suites in this test file, with test methods included.
|
72
|
+
def suites
|
73
|
+
# Since we're not using `ruby -Itest -Ilib` to run the tests, we need to add this directory to the `LOAD_PATH`
|
74
|
+
$:.unshift "./test", "./spec", "./lib"
|
75
|
+
|
76
|
+
begin
|
77
|
+
# Fire up this Ruby file. Let's hope it actually has tests.
|
78
|
+
load testable.file
|
79
|
+
rescue LoadError => e
|
80
|
+
# Fail with a happier error message instead of spitting out a backtrace from this gem
|
81
|
+
STDERR.puts "Failed loading test file:\n#{e.message}"
|
82
|
+
return []
|
83
|
+
end
|
84
|
+
|
85
|
+
suites = runner.suites
|
86
|
+
|
87
|
+
# Use some janky internal APIs to group test methods by test suite.
|
88
|
+
suites.inject({}) do |test_suites, suite_class|
|
89
|
+
# 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
|
90
|
+
if runner.test_methods(suite_class).any?
|
91
|
+
test_suites[suite_class] = runner.test_methods(suite_class)
|
92
|
+
end
|
93
|
+
test_suites
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def runner
|
98
|
+
@runner ||= M::Frameworks.framework_runner
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
data/lib/m/frameworks.rb
CHANGED
@@ -1,5 +1,35 @@
|
|
1
1
|
module M
|
2
2
|
class Frameworks
|
3
|
+
def self.framework_runner
|
4
|
+
new.framework_runner
|
5
|
+
end
|
6
|
+
|
7
|
+
def framework_runner
|
8
|
+
if minitest5?
|
9
|
+
Runners::Minitest5.new
|
10
|
+
elsif minitest4?
|
11
|
+
Runners::Minitest4.new
|
12
|
+
elsif test_unit?
|
13
|
+
Runners::TestUnit.new
|
14
|
+
else
|
15
|
+
Runners::UnsupportedFramework.new
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def minitest5?
|
22
|
+
defined?(Minitest) && Minitest::Unit::VERSION.start_with?("5")
|
23
|
+
end
|
24
|
+
|
25
|
+
def minitest4?
|
26
|
+
defined?(MiniTest)
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_unit?
|
30
|
+
defined?(Test)
|
31
|
+
end
|
32
|
+
|
3
33
|
def self.minitest5?
|
4
34
|
defined?(Minitest) && Minitest::Unit::VERSION.start_with?("5")
|
5
35
|
end
|
@@ -7,5 +37,9 @@ module M
|
|
7
37
|
def self.minitest4?
|
8
38
|
defined?(MiniTest)
|
9
39
|
end
|
40
|
+
|
41
|
+
def self.test_unit?
|
42
|
+
defined?(Test)
|
43
|
+
end
|
10
44
|
end
|
11
45
|
end
|
data/lib/m/parser.rb
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
require_relative 'testable'
|
2
|
+
|
3
|
+
module M
|
4
|
+
class Parser
|
5
|
+
def initialize(argv)
|
6
|
+
@argv = argv
|
7
|
+
@testable = Testable.new
|
8
|
+
end
|
9
|
+
|
10
|
+
def parse
|
11
|
+
# With no arguments,
|
12
|
+
if argv.empty?
|
13
|
+
# Just shell out to `rake test`.
|
14
|
+
exec "rake test"
|
15
|
+
else
|
16
|
+
parse_options! argv
|
17
|
+
|
18
|
+
# Parse out ARGV, it should be coming in in a format like `test/test_file.rb:9`
|
19
|
+
testable.file, testable.line = argv.first.split(':')
|
20
|
+
|
21
|
+
# If this file is a directory, not a file, run the tests inside of this directory
|
22
|
+
if Dir.exist?(testable.file)
|
23
|
+
# Make a new rake test task with a hopefully unique name, and run every test looking file in it
|
24
|
+
require "rake/testtask"
|
25
|
+
Rake::TestTask.new(:m_custom) do |t|
|
26
|
+
t.libs << 'test'
|
27
|
+
t.libs << 'spec'
|
28
|
+
t.test_files = FileList["#{testable.file}/*test*.rb", "#{testable.file}/*spec*.rb"]
|
29
|
+
end
|
30
|
+
# Invoke the rake task and exit, hopefully it'll work!
|
31
|
+
begin
|
32
|
+
Rake::Task['m_custom'].invoke
|
33
|
+
rescue RuntimeError
|
34
|
+
exit
|
35
|
+
ensure
|
36
|
+
exit
|
37
|
+
end
|
38
|
+
else
|
39
|
+
return testable
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
attr_reader :argv, :testable
|
47
|
+
|
48
|
+
def parse_options!(argv)
|
49
|
+
require 'optparse'
|
50
|
+
|
51
|
+
OptionParser.new do |opts|
|
52
|
+
opts.banner = 'Options:'
|
53
|
+
opts.version = M::VERSION
|
54
|
+
|
55
|
+
opts.on '-h', '--help', 'Display this help.' do
|
56
|
+
puts "Usage: m [OPTIONS] [FILES]\n\n", opts
|
57
|
+
exit
|
58
|
+
end
|
59
|
+
|
60
|
+
opts.on '--version', 'Display the version.' do
|
61
|
+
puts "m #{M::VERSION}"
|
62
|
+
exit
|
63
|
+
end
|
64
|
+
|
65
|
+
opts.on '-l', '--line LINE', Integer, 'Line number for file.' do |line|
|
66
|
+
p "parsing line #{line}"
|
67
|
+
testable.line = line
|
68
|
+
end
|
69
|
+
|
70
|
+
opts.parse! argv
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
data/lib/m/runner.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require_relative 'parser'
|
2
|
+
require_relative 'executor'
|
3
|
+
|
4
|
+
### Runners are in charge of running your tests, depending on the framework
|
5
|
+
# Instead of slamming all of this junk in an `M` class, it's here instead.
|
6
|
+
module M
|
7
|
+
class Runner
|
8
|
+
def initialize(argv)
|
9
|
+
@argv = argv
|
10
|
+
end
|
11
|
+
|
12
|
+
# There's two steps to running our tests:
|
13
|
+
# 1. Parsing the given input for the tests we need to find (or groups of tests)
|
14
|
+
# 2. Run those tests we found that match what you wanted
|
15
|
+
def run
|
16
|
+
testable = Parser.new(@argv).parse
|
17
|
+
Executor.new(testable).execute
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module M
|
2
|
+
module Runners
|
3
|
+
class Minitest5 < Base
|
4
|
+
def suites
|
5
|
+
Minitest::Runnable.runnables
|
6
|
+
end
|
7
|
+
|
8
|
+
def run(test_arguments)
|
9
|
+
Minitest.run test_arguments
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_methods(suite_class)
|
13
|
+
suite_class.runnable_methods
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module M
|
2
|
+
module Runners
|
3
|
+
class UnsupportedFramework < Base
|
4
|
+
def suites
|
5
|
+
not_supported
|
6
|
+
[]
|
7
|
+
end
|
8
|
+
|
9
|
+
def run(_test_arguments)
|
10
|
+
not_supported
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def not_supported
|
16
|
+
STDERR.puts "This test framework is not supported! Please open up an issue at https://github.com/qrush/m !"
|
17
|
+
false
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/lib/m/testable.rb
ADDED
data/lib/version.rb
CHANGED
data/m.gemspec
CHANGED
@@ -14,10 +14,10 @@ Gem::Specification.new do |gem|
|
|
14
14
|
|
15
15
|
gem.add_runtime_dependency "method_source", ">= 0.6.7"
|
16
16
|
gem.add_runtime_dependency "rake", ">= 0.9.2.2"
|
17
|
+
|
17
18
|
gem.add_development_dependency "activesupport"
|
18
19
|
gem.add_development_dependency "rdiscount"
|
19
20
|
gem.add_development_dependency "rocco"
|
20
|
-
gem.add_development_dependency "minitest"
|
21
21
|
gem.add_development_dependency "appraisal"
|
22
22
|
|
23
23
|
gem.required_ruby_version = ">= 1.9"
|
data/test/active_support_test.rb
CHANGED
@@ -2,28 +2,28 @@ require 'test_helper'
|
|
2
2
|
|
3
3
|
class ActiveSupportTest < MTest
|
4
4
|
def test_run_simple_test_by_line_number
|
5
|
-
output = m('examples/active_support_example_test.rb:
|
6
|
-
assert_output
|
5
|
+
output = m('examples/active_support_example_test.rb:11')
|
6
|
+
assert_output(/1 tests, 1 assertions/, output)
|
7
7
|
end
|
8
8
|
|
9
9
|
def test_runs_entire_test_without_line_number
|
10
10
|
output = m('examples/active_support_example_test.rb')
|
11
|
-
assert_output
|
11
|
+
assert_output(/4 tests/, output)
|
12
12
|
end
|
13
13
|
|
14
14
|
def test_run_inside_of_test
|
15
|
-
output = m('examples/active_support_example_test.rb:
|
16
|
-
assert_output
|
15
|
+
output = m('examples/active_support_example_test.rb:12')
|
16
|
+
assert_output(/1 tests, 1 assertions/, output)
|
17
17
|
end
|
18
18
|
|
19
19
|
def test_run_on_end_of_test
|
20
|
-
output = m('examples/active_support_example_test.rb:
|
21
|
-
assert_output
|
20
|
+
output = m('examples/active_support_example_test.rb:13')
|
21
|
+
assert_output(/1 tests, 1 assertions/, output)
|
22
22
|
end
|
23
23
|
|
24
24
|
def test_run_inside_big_test
|
25
|
-
output = m('examples/active_support_example_test.rb:
|
26
|
-
assert_output
|
25
|
+
output = m('examples/active_support_example_test.rb:17')
|
26
|
+
assert_output(/1 tests, 3 assertions/, output)
|
27
27
|
end
|
28
28
|
|
29
29
|
def test_run_on_blank_line_orders_tests_by_line_number
|
@@ -33,21 +33,21 @@ class ActiveSupportTest < MTest
|
|
33
33
|
expected = <<-EOF
|
34
34
|
No tests found on line 2. Valid tests to run:
|
35
35
|
|
36
|
-
test_normal: m examples/active_support_example_test.rb:
|
37
|
-
test_carrot: m examples/active_support_example_test.rb:
|
38
|
-
test_daikon: m examples/active_support_example_test.rb:
|
39
|
-
test_eggplant_fig: m examples/active_support_example_test.rb:
|
36
|
+
test_normal: m examples/active_support_example_test.rb:7
|
37
|
+
test_carrot: m examples/active_support_example_test.rb:11
|
38
|
+
test_daikon: m examples/active_support_example_test.rb:15
|
39
|
+
test_eggplant_fig: m examples/active_support_example_test.rb:21
|
40
40
|
EOF
|
41
41
|
assert_equal expected.strip, output
|
42
42
|
end
|
43
43
|
|
44
44
|
def test_run_on_test_with_spaces
|
45
|
-
output = m('examples/active_support_example_test.rb:
|
46
|
-
assert_output
|
45
|
+
output = m('examples/active_support_example_test.rb:21')
|
46
|
+
assert_output(/1 tests, 1 assertions/, output)
|
47
47
|
end
|
48
48
|
|
49
49
|
def test_run_on_test_with_unescaped_regular_express_characters
|
50
|
-
output = m('examples/active_support_unescaped_example_test.rb:
|
51
|
-
assert_output
|
50
|
+
output = m('examples/active_support_unescaped_example_test.rb:7')
|
51
|
+
assert_output(/1 tests, 1 assertions/, output)
|
52
52
|
end
|
53
53
|
end
|
data/test/bench.rb
CHANGED
@@ -1,10 +1,27 @@
|
|
1
|
-
require 'benchmark'
|
2
|
-
|
3
|
-
Benchmark.
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
1
|
+
require 'benchmark/ips'
|
2
|
+
|
3
|
+
Benchmark.ips do |bench|
|
4
|
+
bench.report("running m on a file that doesn't exist") do
|
5
|
+
`ruby -Ilib ./bin/m failwhale 2>/dev/null`
|
6
|
+
end
|
7
|
+
|
8
|
+
bench.report("running m on an empty file") do
|
9
|
+
`ruby -Ilib ./bin/m test/examples/empty_example_test.rb 2>/dev/null`
|
10
|
+
end
|
11
|
+
|
12
|
+
bench.report("running m on an entire file with minitest5") do
|
13
|
+
`appraisal minitest5 ruby -Ilib ./bin/m test/examples/minitest_5_example_test.rb 2>/dev/null`
|
14
|
+
end
|
15
|
+
|
16
|
+
bench.report("running m on an entire file with minitest4") do
|
17
|
+
`appraisal minitest4 ruby -Ilib ./bin/m test/examples/minitest_4_example_test.rb 2>/dev/null`
|
18
|
+
end
|
19
|
+
|
20
|
+
bench.report("running m on a specific test with minitest4") do
|
21
|
+
`appraisal minitest4 ruby -Ilib ./bin/m test/examples/minitest_4_example_test.rb:19 2>/dev/null`
|
22
|
+
end
|
23
|
+
|
24
|
+
bench.report("running m on a specific test with minitest5") do
|
25
|
+
`appraisal minitest5 ruby -Ilib ./bin/m test/examples/minitest_5_example_test.rb:19 2>/dev/null`
|
9
26
|
end
|
10
27
|
end
|