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.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.travis.yml +8 -4
  4. data/Gemfile +1 -0
  5. data/README.md +11 -6
  6. data/Rakefile +10 -1
  7. data/benchmarks/20150430-benchmark.log +26 -0
  8. data/gemfiles/minitest4.gemfile +1 -1
  9. data/gemfiles/minitest4.gemfile.lock +30 -18
  10. data/gemfiles/minitest5.gemfile +1 -1
  11. data/gemfiles/minitest5.gemfile.lock +29 -17
  12. data/lib/m.rb +1 -271
  13. data/lib/m/executor.rb +101 -0
  14. data/lib/m/frameworks.rb +34 -0
  15. data/lib/m/parser.rb +74 -0
  16. data/lib/m/runner.rb +20 -0
  17. data/lib/m/runners/base.rb +17 -0
  18. data/lib/m/runners/minitest_4.rb +13 -0
  19. data/lib/m/runners/minitest_5.rb +17 -0
  20. data/lib/m/runners/test_unit.rb +13 -0
  21. data/lib/m/runners/unsupported_framework.rb +21 -0
  22. data/lib/m/testable.rb +15 -0
  23. data/lib/version.rb +1 -1
  24. data/m.gemspec +1 -1
  25. data/test/active_support_test.rb +17 -17
  26. data/test/bench.rb +25 -8
  27. data/test/empty_test.rb +1 -1
  28. data/test/everything_test.rb +11 -6
  29. data/test/examples/active_support_example_test.rb +0 -1
  30. data/test/examples/active_support_unescaped_example_test.rb +0 -1
  31. data/test/examples/empty_example_test.rb +2 -2
  32. data/test/examples/minitest_4_example_test.rb +4 -6
  33. data/test/examples/minitest_5_example_test.rb +4 -5
  34. data/test/examples/multiple_example_test.rb +0 -1
  35. data/test/examples/subdir/a_test.rb +2 -2
  36. data/test/examples/subdir/b_test.rb +2 -2
  37. data/test/examples/subdir/c_test.rb +2 -2
  38. data/test/examples/subdir_with_failures/a_test.rb +7 -0
  39. data/test/examples/test_unit_example_test.rb +12 -12
  40. data/test/minitest_4_test.rb +13 -13
  41. data/test/minitest_5_test.rb +6 -6
  42. data/test/multiple_test.rb +3 -3
  43. data/test/options_test.rb +6 -6
  44. data/test/test_helper.rb +13 -12
  45. data/test/test_unit_test.rb +29 -27
  46. metadata +13 -16
@@ -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
@@ -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
@@ -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
@@ -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 Base
4
+ def suites
5
+ raise 'Not implemented'
6
+ end
7
+
8
+ def run(_test_arguments)
9
+ raise 'Not implemented'
10
+ end
11
+
12
+ def test_methods(suite_class)
13
+ suite_class.test_methods
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,13 @@
1
+ module M
2
+ module Runners
3
+ class Minitest4 < Base
4
+ def suites
5
+ MiniTest::Unit::TestCase.test_suites
6
+ end
7
+
8
+ def run(test_arguments)
9
+ MiniTest::Unit.runner.run test_arguments
10
+ end
11
+ end
12
+ end
13
+ 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,13 @@
1
+ module M
2
+ module Runners
3
+ class TestUnit < Base
4
+ def suites
5
+ Test::Unit::TestCase.test_suites
6
+ end
7
+
8
+ def run(test_arguments)
9
+ Test::Unit::AutoRunner.run(false, nil, test_arguments)
10
+ end
11
+ end
12
+ end
13
+ 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
@@ -0,0 +1,15 @@
1
+ module M
2
+ class Testable
3
+ attr_accessor :file
4
+ attr_reader :line
5
+
6
+ def initialize(file = "", line = nil)
7
+ @file = file
8
+ @line = line
9
+ end
10
+
11
+ def line=(line)
12
+ @line ||= line.to_i
13
+ end
14
+ end
15
+ end
@@ -1,3 +1,3 @@
1
1
  module M
2
- VERSION = "1.3.3"
2
+ VERSION = "1.3.4"
3
3
  end
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"
@@ -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:12')
6
- assert_output /1 tests, 1 assertions/, 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 /4 tests/, 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:13')
16
- assert_output /1 tests, 1 assertions/, 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:14')
21
- assert_output /1 tests, 1 assertions/, 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:18')
26
- assert_output /1 tests, 3 assertions/, 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:8
37
- test_carrot: m examples/active_support_example_test.rb:12
38
- test_daikon: m examples/active_support_example_test.rb:16
39
- test_eggplant_fig: m examples/active_support_example_test.rb:22
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:22')
46
- assert_output /1 tests, 1 assertions/, 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:8')
51
- assert_output /1 tests, 1 assertions/, 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
@@ -1,10 +1,27 @@
1
- require 'benchmark'
2
-
3
- Benchmark.bmbm do |bench|
4
- TIMES = 100
5
- bench.report("running m on a file that doesn't exist, #{TIMES} times") do
6
- TIMES.times do
7
- `ruby -Ilib ./bin/m failwhale 2>/dev/null`
8
- end
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