Skip to content

Commit 105e05e

Browse files
committed
Parse tests with prism
This changes TestParser to parse with prism instead of ripper if it is available for the current version of Ruby. It's within the margin for the speed, and its significantly less code that is easier to read and should be easier to maintain.
1 parent 11a97e6 commit 105e05e

File tree

2 files changed

+61
-64
lines changed

2 files changed

+61
-64
lines changed

railties/lib/rails/test_unit/test_parser.rb

Lines changed: 17 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -12,38 +12,29 @@ module Rails
1212
module TestUnit
1313
# Parse a test file to extract the line ranges of all tests in both
1414
# method-style (def test_foo) and declarative-style (test "foo" do)
15-
class TestParser < Prism::Visitor
15+
module TestParser
1616
# Helper to translate a method object into the path and line range where
1717
# the method was defined.
1818
def self.definition_for(method)
1919
filepath, start_line = method.source_location
20-
Prism.parse_file(filepath).value.accept(new(ranges = {}))
21-
(end_line = ranges[start_line]) && [filepath, (start_line..end_line)]
22-
end
23-
24-
def self.definitions_for(source, filepath)
25-
Prism.parse(source, filepath: filepath).value.accept(new(ranges = {}))
26-
ranges
27-
end
28-
29-
attr_reader :ranges
30-
31-
def initialize(ranges)
32-
@ranges = ranges
33-
end
34-
35-
def visit_def_node(node)
36-
if node.name.start_with?("test")
37-
ranges[node.location.start_line] = node.location.end_line
20+
queue = [Prism.parse_file(filepath).value]
21+
22+
while (node = queue.shift)
23+
case node.type
24+
when :def_node
25+
if node.name.start_with?("test") && node.location.start_line == start_line
26+
return [filepath, start_line..node.location.end_line]
27+
end
28+
when :call_node
29+
if node.name == :test && node.location.start_line == start_line
30+
return [filepath, start_line..node.location.end_line]
31+
end
32+
end
33+
34+
queue.concat(node.compact_child_nodes)
3835
end
39-
super
40-
end
4136

42-
def visit_call_node(node)
43-
if node.name == :test
44-
ranges[node.location.start_line] = node.location.end_line
45-
end
46-
super
37+
nil
4738
end
4839
end
4940
end

railties/test/test_unit/test_parser_test.rb

Lines changed: 44 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -5,55 +5,61 @@
55
require "active_support/testing/autorun"
66
require "rails/test_unit/test_parser"
77

8-
class TestParserTest < ActiveSupport::TestCase
9-
def test_parser
10-
example_test = <<~RUBY
11-
require "test_helper"
8+
class TestParserTestFixture < ActiveSupport::TestCase
9+
def test_method
10+
assert true
1211

13-
class ExampleTest < ActiveSupport::TestCase
14-
def test_method
15-
assert true
12+
assert true
13+
end
1614

15+
def test_oneline; assert true; end
1716

18-
end
17+
test "declarative" do
18+
assert true
19+
20+
assert true
21+
end
1922

20-
def test_oneline; assert true; end
23+
test("declarative w/parens") do
24+
assert true
25+
end
2126

22-
test "declarative" do
23-
assert true
24-
end
27+
self.test "declarative explicit receiver" do
28+
assert true
2529

26-
test("declarative w/parens") do
27-
assert true
30+
assert true
31+
end
2832

29-
end
33+
test("declarative oneline") { assert true }
3034

31-
self.test "declarative explicit receiver" do
32-
assert true
33-
end
35+
test("declarative oneline do") do assert true end
3436

35-
test("declarative oneline") { assert true }
37+
test("declarative multiline w/ braces") {
38+
assert true
39+
assert_not false
40+
}
41+
end
3642

37-
test("declarative oneline do") do assert true end
43+
class TestParserTest < ActiveSupport::TestCase
44+
def test_parser
45+
actual =
46+
TestParserTestFixture
47+
.instance_methods(false)
48+
.map { |method| TestParserTestFixture.instance_method(method) }
49+
.sort_by { |method| method.source_location[1] }
50+
.map { |method| [method.name, *Rails::TestUnit::TestParser.definition_for(method)] }
3851

39-
test("declarative multiline w/ braces") {
40-
assert true
41-
refute false
42-
}
43-
end
44-
RUBY
52+
expected = [
53+
[:test_method, __FILE__, 9..13],
54+
[:test_oneline, __FILE__, 15..15],
55+
[:test_declarative, __FILE__, 17..21],
56+
[:"test_declarative_w/parens", __FILE__, 23..25],
57+
[:test_declarative_explicit_receiver, __FILE__, 27..31],
58+
[:test_declarative_oneline, __FILE__, 33..33],
59+
[:test_declarative_oneline_do, __FILE__, 35..35],
60+
[:"test_declarative_multiline_w/_braces", __FILE__, 37..40]
61+
]
4562

46-
actual_map = Rails::TestUnit::TestParser.definitions_for(example_test, "example_test.rb")
47-
expected_map = {
48-
4 => 8, # test_method
49-
10 => 10, # test_oneline
50-
12 => 14, # declarative
51-
16 => 19, # declarative w/parens
52-
21 => 23, # declarative explicit receiver
53-
25 => 25, # declarative oneline
54-
27 => 27, # declarative oneilne do
55-
29 => 32 # declarative multiline w/braces
56-
}
57-
assert_equal expected_map, actual_map
63+
assert_equal expected, actual
5864
end
5965
end

0 commit comments

Comments
 (0)