diff --git a/lib/ruby_lsp/test_reporters/lsp_reporter.rb b/lib/ruby_lsp/test_reporters/lsp_reporter.rb index 0c6573d55..510574ca3 100644 --- a/lib/ruby_lsp/test_reporters/lsp_reporter.rb +++ b/lib/ruby_lsp/test_reporters/lsp_reporter.rb @@ -37,6 +37,24 @@ def uri_and_line_for(method_object) zero_based_line = line ? line - 1 : nil [uri, zero_based_line] end + + # VS Code stores uri.fsPath keys; Dir.pwd may differ in separators / drive case (#3881). + #: (String) -> String + def normalize_path(path) + path.tr("\\", "/").sub(/\A([A-Za-z]):/) { "#{Regexp.last_match(1).downcase}:" } + end + + #: (Hash[String, String], ?String) -> String? + def resolve_reporter_port(db, working_directory = Dir.pwd) + return db[working_directory] if db.key?(working_directory) + + normalized_pwd = normalize_path(working_directory) + db.each do |stored_path, port| + return port if normalize_path(stored_path) == normalized_pwd + end + + nil + end end # https://code.visualstudio.com/api/references/vscode-api#Position @@ -64,8 +82,11 @@ def initialize if port socket(port) elsif File.exist?(port_db_path) - db = JSON.load_file(port_db_path) - socket(db[Dir.pwd]) + db = JSON.load_file(port_db_path) #: Hash[String, String] + resolved_port = self.class.resolve_reporter_port(db) + raise "No reporter port for #{Dir.pwd}" unless resolved_port + + socket(resolved_port) else # For tests that don't spawn the TCP server require "stringio" diff --git a/test/test_reporters/lsp_reporter_test.rb b/test/test_reporters/lsp_reporter_test.rb index 354574012..063599006 100644 --- a/test/test_reporters/lsp_reporter_test.rb +++ b/test/test_reporters/lsp_reporter_test.rb @@ -168,5 +168,47 @@ def test_uri_and_line_for_converts_to_zero_based_line assert_equal(one_based_line - 1, line) end + + def test_resolve_reporter_port_matches_windows_path_variants + db = { "d:\\source\\repos\\myproject" => "12345" } + + assert_equal( + "12345", + LspReporter.resolve_reporter_port(db, "D:/source/repos/myproject"), + ) + assert_equal( + "12345", + LspReporter.resolve_reporter_port(db, "d:\\source\\repos\\myproject"), + ) + assert_nil(LspReporter.resolve_reporter_port(db, "D:/other/project")) + end + + def test_socket_connects_using_port_db_despite_windows_path_mismatch + server = TCPServer.new("127.0.0.1", 0) + port = server.addr[1].to_s + thread = Thread.new { server.accept } + io = nil #: Socket? + + Dir.mktmpdir("ruby-lsp-3881-") do |tmpdir| + ENV.delete("RUBY_LSP_REPORTER_PORT") + Dir.stubs(:tmpdir).returns(tmpdir) + Dir.stubs(:pwd).returns("D:/source/repos/myproject") + + port_db_path = File.join(tmpdir, "ruby-lsp", "test_reporter_port_db.json") + FileUtils.mkdir_p(File.dirname(port_db_path)) + File.write(port_db_path, { "d:\\source\\repos\\myproject" => port }.to_json) + + reporter = LspReporter.new + io = reporter.instance_variable_get(:@io) + + assert_kind_of(Socket, io) + + accepted = thread.join(1)&.value #: untyped + accepted&.close + end + ensure + io&.close + server.close + end end end