From 1b2b71ea1cffe3f8810e89b7362a324cbb2e9133 Mon Sep 17 00:00:00 2001 From: Mike Moore Date: Sat, 10 Dec 2022 13:54:39 -0700 Subject: [PATCH] Remove method_source dependency Replace with Rails' TestParser using Ripper. --- lib/m/test_method.rb | 22 ++---------- lib/m/test_parser.rb | 86 ++++++++++++++++++++++++++++++++++++++++++++ m.gemspec | 1 - 3 files changed, 89 insertions(+), 20 deletions(-) create mode 100644 lib/m/test_parser.rb diff --git a/lib/m/test_method.rb b/lib/m/test_method.rb index 0601072..56a5364 100644 --- a/lib/m/test_method.rb +++ b/lib/m/test_method.rb @@ -1,4 +1,4 @@ -require "method_source" +require "m/test_parser" module M ### Simple data structure for what a test method contains. @@ -9,26 +9,10 @@ module M # Includes the name of this method, what line on the file it begins on, # and where it ends. TestMethod = Struct.new :name, :start_line, :end_line do - # Set up a new test method for this test suite class def self.create suite_class, test_method - # Hopefully it's been defined as an instance method, so we'll need to - # look up the ruby Method instance for it method = suite_class.instance_method test_method - - # Ruby can find the starting line for us, so pull that out of the array - start_line = method.source_location.last - - # Ruby can't find the end line however, and I'm too lazy to write - # a parser. Instead, `method_source` adds `Method#source` so we can - # deduce this ourselves. - # - # The end line should be the number of line breaks in the method source, - # added to the starting line and subtracted by one. - - end_line = method.source.split("\n").size + start_line - 1 - - # Shove the given attributes into a new databag - new test_method, start_line, end_line + _file, line_range = TestParser.definition_for method + new test_method, line_range.begin, line_range.end end end end diff --git a/lib/m/test_parser.rb b/lib/m/test_parser.rb new file mode 100644 index 0000000..f1da28f --- /dev/null +++ b/lib/m/test_parser.rb @@ -0,0 +1,86 @@ +require "ripper" + +module M + # Parse a test file to extract the line ranges of all tests in both + # method-style (def test_foo) and declarative-style (test "foo" do) + class TestParser < Ripper + # Brazenly stolen from Rails::TestUnit::TestParser by Carl Brasic @brasic + + # Helper to translate a method object into the path and line range where + # the method was defined. + def self.definition_for(method_obj) + path, begin_line = method_obj.source_location + begins_to_ends = new(File.read(path), path).parse + return unless (end_line = begins_to_ends[begin_line]) + [path, (begin_line..end_line)] + end + + def initialize(*) + # A hash mapping the 1-indexed line numbers that tests start on to where they end. + @begins_to_ends = {} + super + end + + def parse + super + @begins_to_ends + end + + # method test e.g. `def test_some_description` + # This event's first argument gets the `ident` node containing the method + # name, which we have overridden to return the line number of the ident + # instead. + def on_def(begin_line, *) + @begins_to_ends[begin_line] = lineno + end + + # Everything past this point is to support declarative tests, which + # require more work to get right because of the many different ways + # methods can be invoked in ruby, all of which are parsed differently. + # + # The approach is just to store the current line number when the + # "test" method is called and pass it up the tree so it's available at + # the point when we also know the line where the associated block ends. + + def on_method_add_block(begin_line, end_line) + if begin_line && end_line + @begins_to_ends[begin_line] = end_line + end + end + + def on_command_call(*, begin_lineno, _args) + begin_lineno + end + + def first_arg(arg, *) + arg + end + + def just_lineno(*) + lineno + end + + alias_method :on_method_add_arg, :first_arg + alias_method :on_command, :first_arg + alias_method :on_stmts_add, :first_arg + alias_method :on_arg_paren, :first_arg + alias_method :on_bodystmt, :first_arg + + alias_method :on_ident, :just_lineno + alias_method :on_do_block, :just_lineno + alias_method :on_stmts_new, :just_lineno + alias_method :on_brace_block, :just_lineno + + def on_args_new + [] + end + + def on_args_add(parts, part) + parts << part + end + + def on_args_add_block(args, *rest) + args.first + end + end +end diff --git a/m.gemspec b/m.gemspec index befb11c..a13f43d 100644 --- a/m.gemspec +++ b/m.gemspec @@ -12,7 +12,6 @@ Gem::Specification.new do |gem| gem.require_paths = ["lib"] gem.version = M::VERSION - gem.add_runtime_dependency "method_source", ">= 0.6.7" gem.add_runtime_dependency "rake", ">= 0.9.2.2" gem.add_development_dependency "activesupport"