diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index c07a884..59a3867 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -90,6 +90,7 @@ if(BUILD_TESTING) add_test(language_protocol_client_test language_protocol_client_test) add_executable(language_protocol_server_test language_protocol_server_test.cpp) + target_link_libraries(language_protocol_server_test Boost::filesystem) endif() if(BUILD_FUZZING) diff --git a/tests/language_protocol_client_test.cpp b/tests/language_protocol_client_test.cpp index a5bb7dc..de0fcc1 100644 --- a/tests/language_protocol_client_test.cpp +++ b/tests/language_protocol_client_test.cpp @@ -1,4 +1,3 @@ -#include "config.hpp" #include "source_language_protocol.hpp" #include @@ -31,16 +30,61 @@ int main() { view->get_buffer()->insert_at_cursor(" "); g_assert(view->get_buffer()->get_text() == R"( fn main() { - println!("Hello, world!"); + let a = 2; + println!("{}", a); } )"); view->format_style(false); g_assert(view->get_buffer()->get_text() == R"(fn main() { - println!("Hello, world!"); + let a = 2; + println!("{}", a); } )"); + g_assert(view->get_declaration_location); + auto location = view->get_declaration_location(); + g_assert(location); + g_assert(location.file_path == "main.rs"); + g_assert_cmpuint(location.line, ==, 0); + g_assert_cmpuint(location.index, ==, 3); + + g_assert(view->get_type_declaration_location); + location = view->get_type_declaration_location(); + g_assert(location); + g_assert(location.file_path == "main.rs"); + g_assert_cmpuint(location.line, ==, 0); + g_assert_cmpuint(location.index, ==, 4); + + g_assert(view->get_implementation_locations); + auto locations = view->get_implementation_locations(); + g_assert(locations.size() == 2); + g_assert(locations[0].file_path == "main.rs"); + g_assert(locations[1].file_path == "main.rs"); + g_assert_cmpuint(locations[0].line, ==, 0); + g_assert_cmpuint(locations[0].index, ==, 0); + g_assert_cmpuint(locations[1].line, ==, 1); + g_assert_cmpuint(locations[1].index, ==, 0); + + g_assert(view->get_usages); + auto usages = view->get_usages(); + g_assert(usages.size() == 2); + g_assert(usages[0].first.file_path == view->file_path); + g_assert(usages[1].first.file_path == view->file_path); + g_assert_cmpuint(usages[0].first.line, ==, 1); + g_assert_cmpuint(usages[0].first.index, ==, 8); + g_assert_cmpuint(usages[1].first.line, ==, 2); + g_assert_cmpuint(usages[1].first.index, ==, 19); + g_assert(usages[0].second == "let a = 2;"); + g_assert(usages[1].second == "println!("{}", a);"); + + g_assert(view->get_methods); + auto methods = view->get_methods(); + g_assert(methods.size() == 1); + g_assert_cmpuint(methods[0].first.line, ==, 0); + g_assert_cmpuint(methods[0].first.index, ==, 0); + g_assert(methods[0].second == "1: main"); + std::atomic exit_status(-1); view->client->on_exit_status = [&exit_status](int exit_status_) { exit_status = exit_status_; diff --git a/tests/language_protocol_server_test.cpp b/tests/language_protocol_server_test.cpp index ac3aeba..7a73795 100644 --- a/tests/language_protocol_server_test.cpp +++ b/tests/language_protocol_server_test.cpp @@ -1,3 +1,4 @@ +#include #include #include @@ -12,6 +13,9 @@ int main() { _setmode(_fileno(stdout), _O_BINARY); #endif + auto tests_path = boost::filesystem::canonical(JUCI_TESTS_PATH); + auto file_path = boost::filesystem::canonical(tests_path / "language_protocol_test_files" / "main.rs"); + std::string line; try { // Read initialize and respond @@ -29,185 +33,172 @@ int main() { return 1; std::string result = R"({ - "jsonrpc": "2.0", - "id": "0", - "result": { - "capabilities": { - "textDocumentSync": { - "openClose": "true", - "change": "2", - "save": "" - }, - "selectionRangeProvider": "true", - "hoverProvider": "true", - "completionProvider": { - "triggerCharacters": [ - ":", - ".", - "'" - ] - }, - "signatureHelpProvider": { - "triggerCharacters": [ - "(", - "," - ] - }, - "definitionProvider": "true", - "typeDefinitionProvider": "true", - "implementationProvider": "true", - "referencesProvider": "true", - "documentHighlightProvider": "true", - "documentSymbolProvider": "true", - "workspaceSymbolProvider": "true", - "codeActionProvider": { - "codeActionKinds": [ - "", - "quickfix", - "refactor", - "refactor.extract", - "refactor.inline", - "refactor.rewrite" - ], - "resolveProvider": "true" - }, - "codeLensProvider": { - "resolveProvider": "true" - }, - "documentFormattingProvider": "true", - "documentOnTypeFormattingProvider": { - "firstTriggerCharacter": "=", - "moreTriggerCharacter": [ - ".", - ">", - "{" - ] - }, - "renameProvider": { - "prepareProvider": "true" - }, - "foldingRangeProvider": "true", - "workspace": { - "fileOperations": { - "willRename": { - "filters": [ - { - "scheme": "file", - "pattern": { - "glob": "**\/*.rs", - "matches": "file" - } - }, - { - "scheme": "file", - "pattern": { - "glob": "**", - "matches": "folder" - } - } - ] - } + "jsonrpc": "2.0", + "id": "0", + "result": { + "capabilities": { + "textDocumentSync": { + "openClose": "true", + "change": "2", + "save": "" + }, + "selectionRangeProvider": "true", + "hoverProvider": "true", + "completionProvider": { + "triggerCharacters": [":", ".", "'"] + }, + "signatureHelpProvider": { + "triggerCharacters": ["(", ","] + }, + "definitionProvider": "true", + "typeDefinitionProvider": "true", + "implementationProvider": "true", + "referencesProvider": "true", + "documentHighlightProvider": "true", + "documentSymbolProvider": "true", + "workspaceSymbolProvider": "true", + "codeActionProvider": { + "codeActionKinds": [ + "", + "quickfix", + "refactor", + "refactor.extract", + "refactor.inline", + "refactor.rewrite" + ], + "resolveProvider": "true" + }, + "codeLensProvider": { + "resolveProvider": "true" + }, + "documentFormattingProvider": "true", + "documentOnTypeFormattingProvider": { + "firstTriggerCharacter": "=", + "moreTriggerCharacter": [".", ">", "{"] + }, + "renameProvider": { + "prepareProvider": "true" + }, + "foldingRangeProvider": "true", + "workspace": { + "fileOperations": { + "willRename": { + "filters": [ + { + "scheme": "file", + "pattern": { + "glob": "**/*.rs", + "matches": "file" } - }, - "callHierarchyProvider": "true", - "semanticTokensProvider": { - "legend": { - "tokenTypes": [ - "comment", - "keyword", - "string", - "number", - "regexp", - "operator", - "namespace", - "type", - "struct", - "class", - "interface", - "enum", - "enumMember", - "typeParameter", - "function", - "method", - "property", - "macro", - "variable", - "parameter", - "angle", - "arithmetic", - "attribute", - "bitwise", - "boolean", - "brace", - "bracket", - "builtinType", - "characterLiteral", - "colon", - "comma", - "comparison", - "constParameter", - "dot", - "escapeSequence", - "formatSpecifier", - "generic", - "label", - "lifetime", - "logical", - "operator", - "parenthesis", - "punctuation", - "selfKeyword", - "semicolon", - "typeAlias", - "union", - "unresolvedReference" - ], - "tokenModifiers": [ - "documentation", - "declaration", - "definition", - "static", - "abstract", - "deprecated", - "readonly", - "constant", - "controlFlow", - "injected", - "mutable", - "consuming", - "async", - "unsafe", - "attribute", - "trait", - "callable", - "intraDocLink" - ] - }, - "range": "true", - "full": { - "delta": "true" + }, + { + "scheme": "file", + "pattern": { + "glob": "**", + "matches": "folder" } - }, - "experimental": { - "joinLines": "true", - "ssr": "true", - "onEnter": "true", - "parentModule": "true", - "runnables": { - "kinds": [ - "cargo" - ] - }, - "workspaceSymbolScopeKindFiltering": "true" - } + } + ] + } + } + }, + "callHierarchyProvider": "true", + "semanticTokensProvider": { + "legend": { + "tokenTypes": [ + "comment", + "keyword", + "string", + "number", + "regexp", + "operator", + "namespace", + "type", + "struct", + "class", + "interface", + "enum", + "enumMember", + "typeParameter", + "function", + "method", + "property", + "macro", + "variable", + "parameter", + "angle", + "arithmetic", + "attribute", + "bitwise", + "boolean", + "brace", + "bracket", + "builtinType", + "characterLiteral", + "colon", + "comma", + "comparison", + "constParameter", + "dot", + "escapeSequence", + "formatSpecifier", + "generic", + "label", + "lifetime", + "logical", + "operator", + "parenthesis", + "punctuation", + "selfKeyword", + "semicolon", + "typeAlias", + "union", + "unresolvedReference" + ], + "tokenModifiers": [ + "documentation", + "declaration", + "definition", + "static", + "abstract", + "deprecated", + "readonly", + "constant", + "controlFlow", + "injected", + "mutable", + "consuming", + "async", + "unsafe", + "attribute", + "trait", + "callable", + "intraDocLink" + ] }, - "serverInfo": { - "name": "rust-analyzer", - "version": "3022a2c3a 2021-05-25 dev" + "range": "true", + "full": { + "delta": "true" + } + }, + "experimental": { + "joinLines": "true", + "ssr": "true", + "onEnter": "true", + "parentModule": "true", + "runnables": { + "kinds": ["cargo"] }, - "offsetEncoding": "utf-8" + "workspaceSymbolScopeKindFiltering": "true" + } + }, + "serverInfo": { + "name": "rust-analyzer", + "version": "3022a2c3a 2021-05-25 dev" } -})"; + } +} +)"; std::cout << "Content-Length: " << result.size() << "\r\n\r\n" << result; } @@ -224,7 +215,7 @@ int main() { boost::property_tree::ptree pt; boost::property_tree::json_parser::read_json(ss, pt); if(pt.get("method") != "initialized") - return 2; + return 1; } // Read textDocument/didOpen @@ -239,7 +230,7 @@ int main() { boost::property_tree::ptree pt; boost::property_tree::json_parser::read_json(ss, pt); if(pt.get("method") != "textDocument/didOpen") - return 3; + return 1; } // Read textDocument/didChange @@ -254,7 +245,7 @@ int main() { boost::property_tree::ptree pt; boost::property_tree::json_parser::read_json(ss, pt); if(pt.get("method") != "textDocument/didChange") - return 4; + return 1; } // Read and write textDocument/formatting @@ -269,27 +260,28 @@ int main() { boost::property_tree::ptree pt; boost::property_tree::json_parser::read_json(ss, pt); if(pt.get("method") != "textDocument/formatting") - return 5; + return 1; std::string result = R"({ - "jsonrpc": "2.0", - "id": "1", - "result": [ - { - "range": { - "start": { - "line": "0", - "character": "0" - }, - "end": { - "line": "0", - "character": "1" - } - }, - "newText": "" + "jsonrpc": "2.0", + "id": "1", + "result": [ + { + "range": { + "start": { + "line": "0", + "character": "0" + }, + "end": { + "line": "0", + "character": "1" } - ] -})"; + }, + "newText": "" + } + ] +} +)"; std::cout << "Content-Length: " << result.size() << "\r\n\r\n" << result; } @@ -306,7 +298,231 @@ int main() { boost::property_tree::ptree pt; boost::property_tree::json_parser::read_json(ss, pt); if(pt.get("method") != "textDocument/didChange") - return 6; + return 1; + } + + // Read and write textDocument/definition + { + std::getline(std::cin, line); + auto size = std::atoi(line.substr(16).c_str()); + std::getline(std::cin, line); + std::string buffer; + buffer.resize(size); + std::cin.read(&buffer[0], size); + std::stringstream ss(buffer); + boost::property_tree::ptree pt; + boost::property_tree::json_parser::read_json(ss, pt); + if(pt.get("method") != "textDocument/definition") + return 1; + + std::string result = R"({ + "jsonrpc": "2.0", + "id": "2", + "result": [ + { + "uri": "file://main.rs", + "range": { + "start": { + "line": "0", + "character": "3" + }, + "end": { + "line": "0", + "character": "7" + } + } + } + ] +} +)"; + std::cout << "Content-Length: " << result.size() << "\r\n\r\n" + << result; + } + + // Read and write textDocument/typeDefinition + { + std::getline(std::cin, line); + auto size = std::atoi(line.substr(16).c_str()); + std::getline(std::cin, line); + std::string buffer; + buffer.resize(size); + std::cin.read(&buffer[0], size); + std::stringstream ss(buffer); + boost::property_tree::ptree pt; + boost::property_tree::json_parser::read_json(ss, pt); + if(pt.get("method") != "textDocument/typeDefinition") + return 1; + + std::string result = R"({ + "jsonrpc": "2.0", + "id": "3", + "result": [ + { + "uri": "file://main.rs", + "range": { + "start": { + "line": "0", + "character": "4" + }, + "end": { + "line": "0", + "character": "7" + } + } + } + ] +} +)"; + std::cout << "Content-Length: " << result.size() << "\r\n\r\n" + << result; + } + + // Read and write textDocument/implementation + { + std::getline(std::cin, line); + auto size = std::atoi(line.substr(16).c_str()); + std::getline(std::cin, line); + std::string buffer; + buffer.resize(size); + std::cin.read(&buffer[0], size); + std::stringstream ss(buffer); + boost::property_tree::ptree pt; + boost::property_tree::json_parser::read_json(ss, pt); + if(pt.get("method") != "textDocument/implementation") + return 1; + + std::string result = R"({ + "jsonrpc": "2.0", + "id": "4", + "result": [ + { + "uri": "file://main.rs", + "range": { + "start": { + "line": "0", + "character": "0" + }, + "end": { + "line": "0", + "character": "1" + } + } + }, + { + "uri": "file://main.rs", + "range": { + "start": { + "line": "1", + "character": "0" + }, + "end": { + "line": "1", + "character": "1" + } + } + } + ] +} +)"; + std::cout << "Content-Length: " << result.size() << "\r\n\r\n" + << result; + } + + // Read and write textDocument/references + { + std::getline(std::cin, line); + auto size = std::atoi(line.substr(16).c_str()); + std::getline(std::cin, line); + std::string buffer; + buffer.resize(size); + std::cin.read(&buffer[0], size); + std::stringstream ss(buffer); + boost::property_tree::ptree pt; + boost::property_tree::json_parser::read_json(ss, pt); + if(pt.get("method") != "textDocument/references") + return 1; + + std::string result = R"({ + "jsonrpc": "2.0", + "id": "5", + "result": [ + { + "uri": "file://)" + file_path.string() + + R"(", + "range": { + "start": { + "line": "2", + "character": "19" + }, + "end": { + "line": "2", + "character": "20" + } + } + }, + { + "uri": "file://)" + file_path.string() + + R"(", + "range": { + "start": { + "line": "1", + "character": "8" + }, + "end": { + "line": "1", + "character": "9" + } + } + } + ] +} +)"; + std::cout << "Content-Length: " << result.size() << "\r\n\r\n" + << result; + } + + // Read and write textDocument/references + { + std::getline(std::cin, line); + auto size = std::atoi(line.substr(16).c_str()); + std::getline(std::cin, line); + std::string buffer; + buffer.resize(size); + std::cin.read(&buffer[0], size); + std::stringstream ss(buffer); + boost::property_tree::ptree pt; + boost::property_tree::json_parser::read_json(ss, pt); + if(pt.get("method") != "textDocument/documentSymbol") + return 1; + + std::string result = R"({ + "jsonrpc": "2.0", + "id": "6", + "result": [ + { + "name": "main", + "kind": "12", + "tags": "", + "deprecated": "false", + "location": { + "uri": "file://main.rs", + "range": { + "start": { + "line": "0", + "character": "0" + }, + "end": { + "line": "3", + "character": "1" + } + } + } + } + ] +} +)"; + std::cout << "Content-Length: " << result.size() << "\r\n\r\n" + << result; } // Read textDocument/didClose @@ -321,7 +537,7 @@ int main() { boost::property_tree::ptree pt; boost::property_tree::json_parser::read_json(ss, pt); if(pt.get("method") != "textDocument/didClose") - return 7; + return 1; } // Read shutdown and respond @@ -336,12 +552,12 @@ int main() { boost::property_tree::ptree pt; boost::property_tree::json_parser::read_json(ss, pt); if(pt.get("method") != "shutdown") - return 8; + return 1; std::string result = R"({ - "jsonrpc": "2.0", - "id": "2", - "result": {} + "jsonrpc": "2.0", + "id": "7", + "result": {} })"; std::cout << "Content-Length: " << result.size() << "\r\n\r\n" << result; @@ -359,11 +575,11 @@ int main() { boost::property_tree::ptree pt; boost::property_tree::json_parser::read_json(ss, pt); if(pt.get("method") != "exit") - return 9; + return 1; } } catch(const std::exception &e) { std::cerr << e.what() << std::endl; - return 100; + return 2; } } diff --git a/tests/language_protocol_test_files/main.rs b/tests/language_protocol_test_files/main.rs index e7a11a9..5c62ef2 100644 --- a/tests/language_protocol_test_files/main.rs +++ b/tests/language_protocol_test_files/main.rs @@ -1,3 +1,4 @@ fn main() { - println!("Hello, world!"); + let a = 2; + println!("{}", a); }