Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 38 additions & 2 deletions lib/ruby_lsp/requests/go_to_relevant_file.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,46 @@ def perform

#: -> Array[String]
def find_relevant_paths
candidate_paths = Dir.glob(File.join("**", relevant_filename_pattern))
pattern = File.join(search_root, "**", relevant_filename_pattern)
candidate_paths = Dir.glob(pattern)

return [] if candidate_paths.empty?

find_most_similar_with_jaccard(candidate_paths).map { File.join(@workspace_path, _1) }
find_most_similar_with_jaccard(candidate_paths).map { |path| File.expand_path(path, @workspace_path) }
end

# Determine the search roots based on the closest test directories.
# This scopes the search to reduce the number of files that need to be checked.
#: -> String
def search_root
current_path = File.join(".", @path)
current_dir = File.dirname(current_path)
while current_dir != "."
dir_basename = File.basename(current_dir)

# If current directory is a test directory, return its parent as search root
if TEST_KEYWORDS.include?(dir_basename)
return File.dirname(current_dir)
end

# Search the test directories by walking up the directory tree
begin
contains_test_dir = Dir
.entries(current_dir)
.filter { |entry| TEST_KEYWORDS.include?(entry) }
.any? { |entry| File.directory?(File.join(current_dir, entry)) }

return current_dir if contains_test_dir
rescue Errno::EACCES, Errno::ENOENT
# Skip directories we can't read
end

# Move up one level
parent_dir = File.dirname(current_dir)
current_dir = parent_dir
end

"."
end

#: -> String
Expand Down
200 changes: 66 additions & 134 deletions test/requests/go_to_relevant_file_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,151 +4,83 @@
require "test_helper"

class GoToRelevantFileTest < Minitest::Test
def test_when_input_is_test_file_returns_array_of_implementation_file_locations
stub_glob_pattern("**/go_to_relevant_file.rb", ["lib/ruby_lsp/requests/go_to_relevant_file.rb"])

test_file_path = "/workspace/test/requests/go_to_relevant_file_test.rb"
expected = ["/workspace/lib/ruby_lsp/requests/go_to_relevant_file.rb"]

result = RubyLsp::Requests::GoToRelevantFile.new(test_file_path, "/workspace").perform
assert_equal(expected, result)
end

def test_when_input_is_implementation_file_returns_array_of_test_file_locations
pattern =
"**/{{test_,spec_,integration_test_}go_to_relevant_file,go_to_relevant_file{_test,_spec,_integration_test}}.rb"
stub_glob_pattern(pattern, ["test/requests/go_to_relevant_file_test.rb"])

impl_path = "/workspace/lib/ruby_lsp/requests/go_to_relevant_file.rb"
expected = ["/workspace/test/requests/go_to_relevant_file_test.rb"]

result = RubyLsp::Requests::GoToRelevantFile.new(impl_path, "/workspace").perform
assert_equal(expected, result)
end

def test_return_all_file_locations_that_have_the_same_highest_coefficient
pattern = "**/{{test_,spec_,integration_test_}some_feature,some_feature{_test,_spec,_integration_test}}.rb"
matches = [
"test/unit/some_feature_test.rb",
"test/integration/some_feature_test.rb",
]
stub_glob_pattern(pattern, matches)

impl_path = "/workspace/lib/ruby_lsp/requests/some_feature.rb"
expected = [
"/workspace/test/unit/some_feature_test.rb",
"/workspace/test/integration/some_feature_test.rb",
]

result = RubyLsp::Requests::GoToRelevantFile.new(impl_path, "/workspace").perform
assert_equal(expected.sort, result.sort)
end

def test_return_empty_array_when_no_filename_matches
pattern = "**/{{test_,spec_,integration_test_}nonexistent_file,nonexistent_file{_test,_spec,_integration_test}}.rb"
stub_glob_pattern(pattern, [])

file_path = "/workspace/lib/ruby_lsp/requests/nonexistent_file.rb"
result = RubyLsp::Requests::GoToRelevantFile.new(file_path, "/workspace").perform
assert_empty(result)
end

def test_it_finds_implementation_when_file_has_test_suffix
stub_glob_pattern("**/feature.rb", ["lib/feature.rb"])

test_path = "/workspace/test/feature_test.rb"
expected = ["/workspace/lib/feature.rb"]

result = RubyLsp::Requests::GoToRelevantFile.new(test_path, "/workspace").perform
assert_equal(expected, result)
end

def test_it_finds_implementation_when_file_has_spec_suffix
stub_glob_pattern("**/feature.rb", ["lib/feature.rb"])

test_path = "/workspace/spec/feature_spec.rb"
expected = ["/workspace/lib/feature.rb"]

result = RubyLsp::Requests::GoToRelevantFile.new(test_path, "/workspace").perform
assert_equal(expected, result)
def setup
@workspace = Dir.mktmpdir
end

def test_it_finds_implementation_when_file_has_integration_test_suffix
stub_glob_pattern("**/feature.rb", ["lib/feature.rb"])

test_path = "/workspace/test/feature_integration_test.rb"
expected = ["/workspace/lib/feature.rb"]

result = RubyLsp::Requests::GoToRelevantFile.new(test_path, "/workspace").perform
assert_equal(expected, result)
end

def test_it_finds_implementation_when_file_has_test_prefix
stub_glob_pattern("**/feature.rb", ["lib/feature.rb"])

test_path = "/workspace/test/test_feature.rb"
expected = ["/workspace/lib/feature.rb"]

result = RubyLsp::Requests::GoToRelevantFile.new(test_path, "/workspace").perform
assert_equal(expected, result)
end

def test_it_finds_implementation_when_file_has_spec_prefix
stub_glob_pattern("**/feature.rb", ["lib/feature.rb"])

test_path = "/workspace/test/spec_feature.rb"
expected = ["/workspace/lib/feature.rb"]

result = RubyLsp::Requests::GoToRelevantFile.new(test_path, "/workspace").perform
assert_equal(expected, result)
end

def test_it_finds_implementation_when_file_has_integration_test_prefix
stub_glob_pattern("**/feature.rb", ["lib/feature.rb"])

test_path = "/workspace/test/integration_test_feature.rb"
expected = ["/workspace/lib/feature.rb"]

result = RubyLsp::Requests::GoToRelevantFile.new(test_path, "/workspace").perform
assert_equal(expected, result)
def teardown
FileUtils.remove_entry(@workspace)
end

def test_it_finds_tests_for_implementation
pattern = "**/{{test_,spec_,integration_test_}feature,feature{_test,_spec,_integration_test}}.rb"
stub_glob_pattern(pattern, ["test/feature_test.rb"])

impl_path = "/workspace/lib/feature.rb"
expected = ["/workspace/test/feature_test.rb"]

result = RubyLsp::Requests::GoToRelevantFile.new(impl_path, "/workspace").perform
assert_equal(expected, result)
def test_when_input_is_test_file_returns_array_of_implementation_file_locations
Dir.chdir(@workspace) do
lib_dir = File.join(@workspace, "lib/ruby_lsp/requests")
test_dir = File.join(@workspace, "test/requests")
FileUtils.mkdir_p(lib_dir)
FileUtils.mkdir_p(test_dir)

impl_file = File.join(lib_dir, "go_to_relevant_file.rb")
test_file = File.join(test_dir, "go_to_relevant_file_test.rb")
File.write(impl_file, "# impl")
File.write(test_file, "# test")

result = RubyLsp::Requests::GoToRelevantFile.new(test_file, @workspace).perform
assert_equal([impl_file], result)
end
end

def test_it_finds_specs_for_implementation
pattern = "**/{{test_,spec_,integration_test_}feature,feature{_test,_spec,_integration_test}}.rb"
stub_glob_pattern(pattern, ["spec/feature_spec.rb"])

impl_path = "/workspace/lib/feature.rb"
expected = ["/workspace/spec/feature_spec.rb"]

result = RubyLsp::Requests::GoToRelevantFile.new(impl_path, "/workspace").perform
assert_equal(expected, result)
def test_when_input_is_implementation_file_returns_array_of_test_file_locations
Dir.chdir(@workspace) do
lib_dir = File.join(@workspace, "lib/ruby_lsp/requests")
test_dir = File.join(@workspace, "test/requests")
FileUtils.mkdir_p(lib_dir)
FileUtils.mkdir_p(test_dir)

impl_file = File.join(lib_dir, "go_to_relevant_file.rb")
test_file = File.join(test_dir, "go_to_relevant_file_test.rb")
File.write(impl_file, "# impl")
File.write(test_file, "# test")

result = RubyLsp::Requests::GoToRelevantFile.new(impl_file, @workspace).perform
assert_equal([test_file], result)
end
end

def test_it_finds_integration_tests_for_implementation
pattern = "**/{{test_,spec_,integration_test_}feature,feature{_test,_spec,_integration_test}}.rb"
stub_glob_pattern(pattern, ["test/feature_integration_test.rb"])
def test_return_empty_array_when_no_filename_matches
Dir.chdir(@workspace) do
lib_dir = File.join(@workspace, "lib/ruby_lsp/requests")
FileUtils.mkdir_p(lib_dir)

impl_path = "/workspace/lib/feature.rb"
expected = ["/workspace/test/feature_integration_test.rb"]
impl_file = File.join(lib_dir, "nonexistent_file.rb")
File.write(impl_file, "# impl")

result = RubyLsp::Requests::GoToRelevantFile.new(impl_path, "/workspace").perform
assert_equal(expected, result)
result = RubyLsp::Requests::GoToRelevantFile.new(impl_file, @workspace).perform
assert_empty(result)
end
end

private

def stub_glob_pattern(pattern, matches)
Dir.stubs(:glob).with(pattern).returns(matches)
def test_it_finds_multiple_matching_tests
Dir.chdir(@workspace) do
lib_dir = File.join(@workspace, "lib/ruby_lsp/requests")
test_root = File.join(@workspace, "test") # ensure top-level test dir exists
test_unit = File.join(test_root, "unit")
test_int = File.join(test_root, "integration")

FileUtils.mkdir_p(lib_dir)
FileUtils.mkdir_p(test_unit)
FileUtils.mkdir_p(test_int)

impl_file = File.join(lib_dir, "some_feature.rb")
unit_test = File.join(test_unit, "some_feature_test.rb")
int_test = File.join(test_int, "some_feature_test.rb")
[impl_file, unit_test, int_test].each { |f| File.write(f, "# file") }

result = RubyLsp::Requests::GoToRelevantFile.new(impl_file, @workspace).perform

assert_equal(
[unit_test, int_test].sort,
result.sort,
)
end
end
end
Loading