tsl-devkit/lsp-server/test/test_provider/definitions_test.cppm

206 lines
6.9 KiB
C++

module;
export module lsp.test.provider.definitions;
import std;
import lsp.test.framework;
import lsp.provider.text_document.definition;
import lsp.core.dispatcher;
import lsp.manager.manager_hub;
import lsp.scheduler.async_executor;
import lsp.protocol;
import lsp.codec.facade;
import lsp.test.provider.fixtures;
export namespace lsp::test::provider
{
class DefinitionTests
{
public:
static void Register(TestRunner& runner);
private:
static TestResult TestDefinitionResolvesInDocument();
static TestResult TestDefinitionResolvesInWorkspaceIndex();
static TestResult TestDefinitionResolvesInSystemIndex();
};
}
namespace lsp::test::provider
{
namespace
{
struct ProviderEnv
{
scheduler::AsyncExecutor scheduler{ 1 };
manager::ManagerHub hub{};
core::ExecutionContext context;
ProviderEnv()
: context([](core::ServerLifecycleEvent) {}, scheduler, hub)
{
hub.Initialize();
}
};
protocol::ResponseMessage ParseResponse(const std::string& json)
{
auto parsed = codec::Deserialize<protocol::ResponseMessage>(json);
if (!parsed)
{
throw std::runtime_error("Failed to deserialize response");
}
return *parsed;
}
protocol::Position FindPosition(const std::string& content, const std::string& marker)
{
auto pos = content.find(marker);
assertTrue(pos != std::string::npos, "Marker not found in fixture");
protocol::Position result{};
result.line = 0;
result.character = 0;
for (std::size_t i = 0; i < pos; ++i)
{
if (content[i] == '\n')
{
result.line++;
result.character = 0;
}
else
{
result.character++;
}
}
return result;
}
void OpenDocument(manager::ManagerHub& hub, const std::string& uri, const std::string& text, int version)
{
protocol::DidOpenTextDocumentParams open_params;
open_params.textDocument.uri = uri;
open_params.textDocument.languageId = "tsl";
open_params.textDocument.version = version;
open_params.textDocument.text = text;
hub.documents().OpenDocument(open_params);
}
protocol::Location ExtractLocation(const protocol::ResponseMessage& response)
{
if (!response.result.has_value())
{
throw std::runtime_error("Expected definition result");
}
return codec::FromLSPAny.template operator()<protocol::Location>(response.result.value());
}
bool LocationMatchesLine(const protocol::Location& location, std::uint32_t line)
{
return location.range.start.line == line;
}
}
void DefinitionTests::Register(TestRunner& runner)
{
runner.addTest("definition resolves in document", TestDefinitionResolvesInDocument);
runner.addTest("definition resolves in workspace index", TestDefinitionResolvesInWorkspaceIndex);
runner.addTest("definition resolves in system index", TestDefinitionResolvesInSystemIndex);
}
TestResult DefinitionTests::TestDefinitionResolvesInDocument()
{
TestResult result{ "", true, "ok" };
ProviderEnv env;
auto path = FixturePath("main_unit.tsf");
auto content = ReadTextFile(path);
auto uri = ToUri(path);
OpenDocument(env.hub, uri, content, 1);
protocol::DefinitionParams params;
params.textDocument.uri = uri;
params.position = FindPosition(content, "UnitFunc(1);");
protocol::RequestMessage request;
request.id = "1";
request.method = "textDocument/definition";
request.params = codec::ToLSPAny(params);
::lsp::provider::text_document::Definition handler;
auto json = handler.ProvideResponse(request, env.context);
auto response = ParseResponse(json);
auto location = ExtractLocation(response);
auto def_pos = FindPosition(content, "function UnitFunc");
assertTrue(LocationMatchesLine(location, def_pos.line), "Definition should resolve in document");
return result;
}
TestResult DefinitionTests::TestDefinitionResolvesInWorkspaceIndex()
{
TestResult result{ "", true, "ok" };
ProviderEnv env;
auto workspace_root = FixturePath("workspace");
env.hub.symbols().LoadWorkspace(ToUri(workspace_root));
auto path = FixturePath("main_unit.tsf");
auto content = ReadTextFile(path);
auto uri = ToUri(path);
OpenDocument(env.hub, uri, content, 1);
protocol::DefinitionParams params;
params.textDocument.uri = uri;
params.position = FindPosition(content, "WorkspaceFunc()");
protocol::RequestMessage request;
request.id = "2";
request.method = "textDocument/definition";
request.params = codec::ToLSPAny(params);
::lsp::provider::text_document::Definition handler;
auto json = handler.ProvideResponse(request, env.context);
auto response = ParseResponse(json);
auto location = ExtractLocation(response);
auto expected = ReadTextFile(FixturePath("workspace/workspace_script.tsl"));
auto expected_pos = FindPosition(expected, "function WorkspaceFunc");
assertTrue(LocationMatchesLine(location, expected_pos.line), "Definition should resolve in workspace index");
return result;
}
TestResult DefinitionTests::TestDefinitionResolvesInSystemIndex()
{
TestResult result{ "", true, "ok" };
ProviderEnv env;
auto system_root = FixturePath("system");
env.hub.symbols().LoadSystemLibrary(system_root.string());
auto path = FixturePath("main_unit.tsf");
auto content = ReadTextFile(path);
auto uri = ToUri(path);
OpenDocument(env.hub, uri, content, 1);
protocol::DefinitionParams params;
params.textDocument.uri = uri;
params.position = FindPosition(content, "SystemUnit");
protocol::RequestMessage request;
request.id = "3";
request.method = "textDocument/definition";
request.params = codec::ToLSPAny(params);
::lsp::provider::text_document::Definition handler;
auto json = handler.ProvideResponse(request, env.context);
auto response = ParseResponse(json);
auto location = ExtractLocation(response);
auto expected = ReadTextFile(FixturePath("system/SystemUnit.tsf"));
auto expected_pos = FindPosition(expected, "unit SystemUnit");
assertTrue(LocationMatchesLine(location, expected_pos.line), "Definition should resolve in system index");
return result;
}
}