forked from OSchip/llvm-project
217 lines
7.9 KiB
C++
217 lines
7.9 KiB
C++
//===-- PathMappingTests.cpp ------------------------*- C++ -*-----------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "PathMapping.h"
|
|
#include "llvm/Support/JSON.h"
|
|
#include "gmock/gmock.h"
|
|
#include "gtest/gtest.h"
|
|
#include <string>
|
|
namespace clang {
|
|
namespace clangd {
|
|
namespace {
|
|
using ::testing::ElementsAre;
|
|
MATCHER_P2(Mapping, ClientPath, ServerPath, "") {
|
|
return arg.ClientPath == ClientPath && arg.ServerPath == ServerPath;
|
|
}
|
|
|
|
bool failedParse(llvm::StringRef RawMappings) {
|
|
llvm::Expected<PathMappings> Mappings = parsePathMappings(RawMappings);
|
|
if (!Mappings) {
|
|
consumeError(Mappings.takeError());
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
TEST(ParsePathMappingTests, WindowsPath) {
|
|
// Relative path to C drive
|
|
EXPECT_TRUE(failedParse(R"(C:a=/root)"));
|
|
EXPECT_TRUE(failedParse(R"(\C:a=/root)"));
|
|
// Relative path to current drive.
|
|
EXPECT_TRUE(failedParse(R"(\a=/root)"));
|
|
// Absolute paths
|
|
llvm::Expected<PathMappings> ParsedMappings =
|
|
parsePathMappings(R"(C:\a=/root)");
|
|
ASSERT_TRUE(bool(ParsedMappings));
|
|
EXPECT_THAT(*ParsedMappings, ElementsAre(Mapping("/C:/a", "/root")));
|
|
// Absolute UNC path
|
|
ParsedMappings = parsePathMappings(R"(\\Server\C$=/root)");
|
|
ASSERT_TRUE(bool(ParsedMappings));
|
|
EXPECT_THAT(*ParsedMappings, ElementsAre(Mapping("//Server/C$", "/root")));
|
|
}
|
|
|
|
TEST(ParsePathMappingTests, UnixPath) {
|
|
// Relative unix path
|
|
EXPECT_TRUE(failedParse("a/b=/root"));
|
|
// Absolute unix path
|
|
llvm::Expected<PathMappings> ParsedMappings = parsePathMappings("/A/b=/root");
|
|
ASSERT_TRUE(bool(ParsedMappings));
|
|
EXPECT_THAT(*ParsedMappings, ElementsAre(Mapping("/A/b", "/root")));
|
|
// Absolute unix path w/ backslash
|
|
ParsedMappings = parsePathMappings(R"(/a/b\\ar=/root)");
|
|
ASSERT_TRUE(bool(ParsedMappings));
|
|
EXPECT_THAT(*ParsedMappings, ElementsAre(Mapping(R"(/a/b\\ar)", "/root")));
|
|
}
|
|
|
|
TEST(ParsePathMappingTests, ImproperFormat) {
|
|
// uneven mappings
|
|
EXPECT_TRUE(failedParse("/home/myuser1="));
|
|
// mappings need to be absolute
|
|
EXPECT_TRUE(failedParse("home/project=/workarea/project"));
|
|
// duplicate delimiter
|
|
EXPECT_TRUE(failedParse("/home==/workarea"));
|
|
// no delimiter
|
|
EXPECT_TRUE(failedParse("/home"));
|
|
// improper delimiter
|
|
EXPECT_TRUE(failedParse("/home,/workarea"));
|
|
}
|
|
|
|
TEST(ParsePathMappingTests, ParsesMultiple) {
|
|
std::string RawPathMappings =
|
|
"/home/project=/workarea/project,/home/project/.includes=/opt/include";
|
|
auto Parsed = parsePathMappings(RawPathMappings);
|
|
ASSERT_TRUE(bool(Parsed));
|
|
EXPECT_THAT(*Parsed,
|
|
ElementsAre(Mapping("/home/project", "/workarea/project"),
|
|
Mapping("/home/project/.includes", "/opt/include")));
|
|
}
|
|
|
|
bool mapsProperly(llvm::StringRef Orig, llvm::StringRef Expected,
|
|
llvm::StringRef RawMappings, PathMapping::Direction Dir) {
|
|
llvm::Expected<PathMappings> Mappings = parsePathMappings(RawMappings);
|
|
if (!Mappings)
|
|
return false;
|
|
llvm::Optional<std::string> MappedPath = doPathMapping(Orig, Dir, *Mappings);
|
|
std::string Actual = MappedPath ? *MappedPath : Orig.str();
|
|
EXPECT_STREQ(Expected.str().c_str(), Actual.c_str());
|
|
return Expected == Actual;
|
|
}
|
|
|
|
TEST(DoPathMappingTests, PreservesOriginal) {
|
|
// Preserves original path when no mapping
|
|
EXPECT_TRUE(mapsProperly("file:///home", "file:///home", "",
|
|
PathMapping::Direction::ClientToServer));
|
|
}
|
|
|
|
TEST(DoPathMappingTests, UsesFirstMatch) {
|
|
EXPECT_TRUE(mapsProperly("file:///home/foo.cpp", "file:///workarea1/foo.cpp",
|
|
"/home=/workarea1,/home=/workarea2",
|
|
PathMapping::Direction::ClientToServer));
|
|
}
|
|
|
|
TEST(DoPathMappingTests, IgnoresSubstrings) {
|
|
// Doesn't map substrings that aren't a proper path prefix
|
|
EXPECT_TRUE(mapsProperly("file://home/foo-bar.cpp", "file://home/foo-bar.cpp",
|
|
"/home/foo=/home/bar",
|
|
PathMapping::Direction::ClientToServer));
|
|
}
|
|
|
|
TEST(DoPathMappingTests, MapsOutgoingPaths) {
|
|
// When IsIncoming is false (i.e.a response), map the other way
|
|
EXPECT_TRUE(mapsProperly("file:///workarea/foo.cpp", "file:///home/foo.cpp",
|
|
"/home=/workarea",
|
|
PathMapping::Direction::ServerToClient));
|
|
}
|
|
|
|
TEST(DoPathMappingTests, OnlyMapFileUris) {
|
|
EXPECT_TRUE(mapsProperly("test:///home/foo.cpp", "test:///home/foo.cpp",
|
|
"/home=/workarea",
|
|
PathMapping::Direction::ClientToServer));
|
|
}
|
|
|
|
TEST(DoPathMappingTests, RespectsCaseSensitivity) {
|
|
EXPECT_TRUE(mapsProperly("file:///HOME/foo.cpp", "file:///HOME/foo.cpp",
|
|
"/home=/workarea",
|
|
PathMapping::Direction::ClientToServer));
|
|
}
|
|
|
|
TEST(DoPathMappingTests, MapsWindowsPaths) {
|
|
// Maps windows properly
|
|
EXPECT_TRUE(mapsProperly("file:///C:/home/foo.cpp",
|
|
"file:///C:/workarea/foo.cpp", R"(C:\home=C:\workarea)",
|
|
PathMapping::Direction::ClientToServer));
|
|
}
|
|
|
|
TEST(DoPathMappingTests, MapsWindowsUnixInterop) {
|
|
// Path mappings with a windows-style client path and unix-style server path
|
|
EXPECT_TRUE(mapsProperly(
|
|
"file:///C:/home/foo.cpp", "file:///workarea/foo.cpp",
|
|
R"(C:\home=/workarea)", PathMapping::Direction::ClientToServer));
|
|
}
|
|
|
|
TEST(ApplyPathMappingTests, PreservesOriginalParams) {
|
|
auto Params = llvm::json::parse(R"({
|
|
"textDocument": {"uri": "file:///home/foo.cpp"},
|
|
"position": {"line": 0, "character": 0}
|
|
})");
|
|
ASSERT_TRUE(bool(Params));
|
|
llvm::json::Value ExpectedParams = *Params;
|
|
PathMappings Mappings;
|
|
applyPathMappings(*Params, PathMapping::Direction::ClientToServer, Mappings);
|
|
EXPECT_EQ(*Params, ExpectedParams);
|
|
}
|
|
|
|
TEST(ApplyPathMappingTests, MapsAllMatchingPaths) {
|
|
// Handles nested objects and array values
|
|
auto Params = llvm::json::parse(R"({
|
|
"rootUri": {"uri": "file:///home/foo.cpp"},
|
|
"workspaceFolders": ["file:///home/src", "file:///tmp"]
|
|
})");
|
|
auto ExpectedParams = llvm::json::parse(R"({
|
|
"rootUri": {"uri": "file:///workarea/foo.cpp"},
|
|
"workspaceFolders": ["file:///workarea/src", "file:///tmp"]
|
|
})");
|
|
auto Mappings = parsePathMappings("/home=/workarea");
|
|
ASSERT_TRUE(bool(Params) && bool(ExpectedParams) && bool(Mappings));
|
|
applyPathMappings(*Params, PathMapping::Direction::ClientToServer, *Mappings);
|
|
EXPECT_EQ(*Params, *ExpectedParams);
|
|
}
|
|
|
|
TEST(ApplyPathMappingTests, MapsOutbound) {
|
|
auto Params = llvm::json::parse(R"({
|
|
"id": 1,
|
|
"result": [
|
|
{"uri": "file:///opt/include/foo.h"},
|
|
{"uri": "file:///workarea/src/foo.cpp"}]
|
|
})");
|
|
auto ExpectedParams = llvm::json::parse(R"({
|
|
"id": 1,
|
|
"result": [
|
|
{"uri": "file:///home/.includes/foo.h"},
|
|
{"uri": "file:///home/src/foo.cpp"}]
|
|
})");
|
|
auto Mappings =
|
|
parsePathMappings("/home=/workarea,/home/.includes=/opt/include");
|
|
ASSERT_TRUE(bool(Params) && bool(ExpectedParams) && bool(Mappings));
|
|
applyPathMappings(*Params, PathMapping::Direction::ServerToClient, *Mappings);
|
|
EXPECT_EQ(*Params, *ExpectedParams);
|
|
}
|
|
|
|
TEST(ApplyPathMappingTests, MapsKeys) {
|
|
auto Params = llvm::json::parse(R"({
|
|
"changes": {
|
|
"file:///home/foo.cpp": {"newText": "..."},
|
|
"file:///home/src/bar.cpp": {"newText": "..."}
|
|
}
|
|
})");
|
|
auto ExpectedParams = llvm::json::parse(R"({
|
|
"changes": {
|
|
"file:///workarea/foo.cpp": {"newText": "..."},
|
|
"file:///workarea/src/bar.cpp": {"newText": "..."}
|
|
}
|
|
})");
|
|
auto Mappings = parsePathMappings("/home=/workarea");
|
|
ASSERT_TRUE(bool(Params) && bool(ExpectedParams) && bool(Mappings));
|
|
applyPathMappings(*Params, PathMapping::Direction::ClientToServer, *Mappings);
|
|
EXPECT_EQ(*Params, *ExpectedParams);
|
|
}
|
|
|
|
} // namespace
|
|
} // namespace clangd
|
|
} // namespace clang
|