From 64ec88f0c38d438d0bdf8dfc7d5f8670795f15d3 Mon Sep 17 00:00:00 2001 From: csh Date: Sun, 2 Nov 2025 15:47:48 +0800 Subject: [PATCH] =?UTF-8?q?:wastebasket:=20=E7=A7=BB=E9=99=A4`test=5Fglaz`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit :sparkles: 新增测试`test_lsp_any` --- lsp-server/test/test_glaz/test_glaz.cpp | 308 ------- lsp-server/test/test_glaz/test_lspany.cpp | 848 ------------------ lsp-server/test/test_lsp_any/CMakeLists.txt | 72 ++ lsp-server/test/test_lsp_any/common_test.cpp | 325 +++++++ lsp-server/test/test_lsp_any/common_test.hpp | 39 + lsp-server/test/test_lsp_any/facade_test.cpp | 389 ++++++++ lsp-server/test/test_lsp_any/facade_test.hpp | 36 + lsp-server/test/test_lsp_any/lsp_any_test.cpp | 689 ++++++++++++++ lsp-server/test/test_lsp_any/lsp_any_test.hpp | 62 ++ .../test/test_lsp_any/test_framework.cpp | 178 ++++ .../test/test_lsp_any/test_framework.hpp | 50 ++ lsp-server/test/test_lsp_any/test_main.cpp | 36 + .../test/test_lsp_any/transformer_test.cpp | 654 ++++++++++++++ .../test/test_lsp_any/transformer_test.hpp | 62 ++ 14 files changed, 2592 insertions(+), 1156 deletions(-) delete mode 100644 lsp-server/test/test_glaz/test_glaz.cpp delete mode 100644 lsp-server/test/test_glaz/test_lspany.cpp create mode 100644 lsp-server/test/test_lsp_any/CMakeLists.txt create mode 100644 lsp-server/test/test_lsp_any/common_test.cpp create mode 100644 lsp-server/test/test_lsp_any/common_test.hpp create mode 100644 lsp-server/test/test_lsp_any/facade_test.cpp create mode 100644 lsp-server/test/test_lsp_any/facade_test.hpp create mode 100644 lsp-server/test/test_lsp_any/lsp_any_test.cpp create mode 100644 lsp-server/test/test_lsp_any/lsp_any_test.hpp create mode 100644 lsp-server/test/test_lsp_any/test_framework.cpp create mode 100644 lsp-server/test/test_lsp_any/test_framework.hpp create mode 100644 lsp-server/test/test_lsp_any/test_main.cpp create mode 100644 lsp-server/test/test_lsp_any/transformer_test.cpp create mode 100644 lsp-server/test/test_lsp_any/transformer_test.hpp diff --git a/lsp-server/test/test_glaz/test_glaz.cpp b/lsp-server/test/test_glaz/test_glaz.cpp deleted file mode 100644 index 435ad70..0000000 --- a/lsp-server/test/test_glaz/test_glaz.cpp +++ /dev/null @@ -1,308 +0,0 @@ -#include -#include -#include "../../src/protocol/protocol.hpp" -#include "../../src/protocol/transform/facade.hpp" - -using namespace lsp::protocol; -using namespace lsp; - -template -void print_json(const string& name, const T& obj) -{ - if (auto result = glz::write_json(obj); result) - { - std::cout << name << " = " << *result << std::endl; - } - else - { - std::cerr << "Error: " << result.error() << "\n"; - } -} - -void test_basic_conversion() -{ - std::cout << "\n=== Testing Basic Conversion ===" << std::endl; - - // 测试 struct 转换 - RequestMessage msg; - msg.method = "textDocument/completion"; - msg.id = 42; - - // Struct -> LSPAny - LSPAny any = transform::LSPAny(msg); - std::string json; - auto ce = glz::write_json(any, json); - if (ce) - std::cout << "Error" << std::endl; - std::cout << "Message as JSON: " << json << std::endl; - - // LSPAny -> Struct - RequestMessage restored = transform::As(any); - std::cout << "Restored method: " << restored.method << std::endl; - std::cout << "id = "; - std::visit([](const auto& value) { - std::cout << value; - }, - restored.id); - std::cout << std::endl; -} - -void test_vector_conversion() -{ - std::cout << "\n=== Testing Vector Conversion ===" << std::endl; - - std::vector positions = { - { 10, 5 }, - { 20, 15 }, - { 30, 25 } - }; - - // Vector -> LSPAny - LSPAny any = transform::LSPAny(positions); - - std::string json; - auto ce = glz::write_json(any, json); - std::cout << "Vector as JSON: " << json << std::endl; - - // LSPAny -> Vector - auto restored = transform::As>(any); - std::cout << "Restored " << restored.size() << " positions" << std::endl; -} - -void test_json_string() -{ - std::cout << "\n=== Test json_string ===" << std::endl; - - string json = R"({"jsonrpc":"2.0","id":1,"method":"initialize","params":{"processId":null,"rootUri":"file:///tmp","workDoneToken":"abc"}})"; - RequestMessage request; - auto ret = glz::read_json(request, json); - std::cout << "jsonrpc = " << request.jsonrpc << std::endl; - std::cout << "method = " << request.method << std::endl; - std::cout << "id = "; - std::visit([](const auto& value) { - std::cout << value; - }, - request.id); - std::cout << std::endl; - - auto result = glz::write_json(request.params); - std::cout << *result << std::endl; - InitializeParams params; - // std::string buffer = R"({"workDoneToken":123,"processId":"id","clientInfo":{"name":"VSCode","version":"1.0.0"},"rootPath":"456","initializationOptions":"options","trace":"messages"})"; - auto r2 = glz::read_json(params, *result); - if (params.processId.has_value()) - std::cout << "processId = " << params.processId.value() << "\n"; - else - std::cout << "processId = null\n"; - - std::cout << std::endl; -} - -void test_basic_types() -{ - std::cout << "\n=== Test basic_types.hpp ===" << std::endl; - Range range = { - .start = { 1, 3 }, - .end = { 2, 4 } - }; - - DocumentFilter df = { - .language = "en", - .scheme = "scheme", - .pattern = "pattern", - }; - - AnnotatedTextEdit ate; - ate.range = { .start = { 1, 3 }, .end = { 2, 4 } }; - ate.newText = "text_edit"; - ate.annotationId = "id"; - - Location location = { - .uri = "location_uri", - .range = { .start = { 1, 3 }, .end = { 2, 4 } } - }; - - Command command1 = { - .title = "command_title", - .command = "command_command", - }; - Command command2 = { - .title = "command_title", - .command = "command_command", - .arguments = "any_string" - }; - Command command3 = { - .title = "command_title", - .command = "command_command", - .arguments = 123 - }; - std::vector array = { - 1.0, // decimal - string("two"), - 3.0, // decimal - true, - nullptr - }; - LSPAny array_val = array; - LSPObject object = { - { "name", string("test object") }, - { "count", 42.0 }, // decimal - { "active", boolean(true) }, - { "data", array_val }, - { "metadata", nullptr } - }; - Command command4 = { - .title = "command_title", - .command = "command_command", - .arguments = object - }; - - MarkdownClientCapabilities mkcp = { - .parser = "parse", - .allowedTags = std::vector{ "h1", "h2", "p", "code", "pre" } - }; - std::cout << "Test....basic_types" << std::endl; - print_json("Range", range); - print_json("DocumentFilter", df); - print_json("AnnotatedTextEdit", ate); - print_json("Location", location); - print_json("Command1", command1); - print_json("Command2", command2); - print_json("Command3", command3); - print_json("Command4", command4); - print_json("MarkdownClientCapabilities", mkcp); - std::cout << std::endl; -} - -void test_capabilities() -{ - std::cout << "\n=== Test capabilities.hpp ===" << std::endl; - InitializeParams ip; - ip.workDoneToken = 123; - ip.processId = std::nullopt; - ip.clientInfo = InitializeParams::ClientInfo{ "VSCode", "1.0.0" }; - // ip.rootPath = "456"; - ip.initializationOptions = "options"; - ip.trace = TraceValueLiterals::Messages; - - std::cout << "Test....capabilities" << std::endl; - print_json("InitializeParams", ip); - std::cout << std::endl; -} - -void test_RequestMessage_serialize() -{ - // // 1. 直接从 LSPObject 转换 - // lsp::protocol::LSPObject obj = [> ... <]; - // auto result = lsp::transform::As(obj); - - // // 2. 从 variant 转换 - // std::variant var = [> ... <]; - // auto result = lsp::transform::As(var); - - // // 3. 从 optional 转换 - // std::optional opt = [> ... <]; - // auto result = lsp::transform::As(opt); // 返回 optional - - // // 4. 从复杂的嵌套类型转换 - // std::optional> complex = [> ... <]; - // auto result = lsp::transform::As(complex); // 返回 optional - - // // 5. 实际使用场景 - // if (request.params.has_value()) { - // // 直接处理 optional> - // auto completionParams = lsp::transform::As(request.params); - // if (completionParams.has_value()) { - // std::cout << "URI: " << completionParams->textDocument.uri << std::endl; - // } - // } - std::string json = R"({ - "jsonrpc":"2.0", - "id":"4", - "method":"textDocument/completion", - "params":{ - "context":{"triggerKind":1}, - "partialResultToken":0, - "position":{"character":12,"line":22}, - "textDocument":{"uri":"file://path/to_file.ts"}, - "workDoneToken":0 - } - })"; - - RequestMessage request; - auto error = glz::read_json(request, json); - - if (error) - { - std::cerr << "Failed to parse JSON: " << glz::format_error(error, json) << std::endl; - } - - std::cout << "jsonrpc: " << request.jsonrpc << std::endl; - std::cout << "method: " << request.method << std::endl; - std::visit([](const auto& id) { - using T = std::decay_t; - if constexpr (std::is_same_v) - { - std::cout << "ID (string): " << id << std::endl; - } - else if constexpr (std::is_same_v) - { - std::cout << "ID (integer): " << id << std::endl; - } - }, - request.id); - - auto completionParams = transform::As(request.params.value()); - std::cout << "URI: " << completionParams.textDocument.uri << std::endl; - - // std::visit([](const auto& value) - // { - // std::cout << value; - // }, request.id); - - // std::visit([](const auto& params) { - - // }, request.params.value()); -} - -void test_message() -{ - std::cout << "\n=== Test message.hpp ===" << std::endl; - ResponseMessage response; - response.id = "123"; - InitializeResult result; - result.serverInfo.name = "TSL Language Server"; - result.serverInfo.version = "1.0.0"; - TextDocumentSyncOptions opts; - opts.openClose = true; - opts.change = TextDocumentSyncKind::Incremental; - result.capabilities.textDocumentSync = opts; - - CompletionParams comparams; - comparams.textDocument.uri = "file://path/to_file.ts"; - comparams.position.character = 12; - comparams.position.line = 22; - comparams.context = CompletionContext{ - .triggerKind = CompletionTriggerKind::Invoked, - .triggerCharacter = std::nullopt - }; - RequestMessage rm; - rm.id = "4"; - rm.method = "textDocument/completion"; - rm.params = transform::LSPObject(comparams); - - print_json("ResponseMessage", response); - print_json("rm", rm); -} - -int main() -{ - // test_basic_conversion(); - // test_vector_conversion(); - // test_json_string(); - // test_basic_types(); - // test_capabilities(); - test_message(); - test_RequestMessage_serialize(); - return 0; -} diff --git a/lsp-server/test/test_glaz/test_lspany.cpp b/lsp-server/test/test_glaz/test_lspany.cpp deleted file mode 100644 index d07e811..0000000 --- a/lsp-server/test/test_glaz/test_lspany.cpp +++ /dev/null @@ -1,848 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include - -using integer = std::int32_t; -using uinteger = std::uint32_t; -using decimal = double; -using boolean = bool; -using string = std::string; - -// 前向声明 -struct LSPAny; -using LSPObject = std::map; -using LSPArray = std::vector; - -using LSPAnyVariant = std::variant< - LSPObject, - LSPArray, - string, - decimal, // 放在前面,优先匹配数字 - boolean, - std::nullptr_t>; - -struct LSPAny -{ - LSPAnyVariant value; - - // 默认构造函数 - LSPAny() : - value(nullptr) {} - - // 拷贝和移动构造函数 - LSPAny(const LSPAny& other) : - value(other.value) {} - LSPAny(LSPAny&& other) noexcept : - value(std::move(other.value)) {} - - // 针对每种支持的类型的构造函数 - LSPAny(const std::map& val) : - value(val) {} - LSPAny(std::map&& val) : - value(std::move(val)) {} - - LSPAny(const std::vector& val) : - value(val) {} - LSPAny(std::vector&& val) : - value(std::move(val)) {} - - LSPAny(const string& val) : - value(val) {} - LSPAny(string&& val) : - value(std::move(val)) {} - LSPAny(const char* val) : - value(string(val)) {} - - // 所有数字类型都转换为 decimal - LSPAny(int val) : - value(static_cast(val)) {} - LSPAny(long val) : - value(static_cast(val)) {} - LSPAny(long long val) : - value(static_cast(val)) {} - LSPAny(unsigned int val) : - value(static_cast(val)) {} - LSPAny(unsigned long val) : - value(static_cast(val)) {} - LSPAny(unsigned long long val) : - value(static_cast(val)) {} - LSPAny(float val) : - value(static_cast(val)) {} - LSPAny(double val) : - value(val) {} - LSPAny(long double val) : - value(static_cast(val)) {} - - LSPAny(boolean val) : - value(val) {} - LSPAny(std::nullptr_t) : - value(nullptr) {} - - // 赋值操作符 - LSPAny& operator=(const LSPAny& other) - { - value = other.value; - return *this; - } - - LSPAny& operator=(LSPAny&& other) noexcept - { - value = std::move(other.value); - return *this; - } - - // 针对每种支持的类型的赋值操作符 - LSPAny& operator=(const std::map& val) - { - value = val; - return *this; - } - - LSPAny& operator=(std::map&& val) - { - value = std::move(val); - return *this; - } - - LSPAny& operator=(const std::vector& val) - { - value = val; - return *this; - } - - LSPAny& operator=(std::vector&& val) - { - value = std::move(val); - return *this; - } - - LSPAny& operator=(const string& val) - { - value = val; - return *this; - } - - LSPAny& operator=(string&& val) - { - value = std::move(val); - return *this; - } - - LSPAny& operator=(const char* val) - { - value = string(val); - return *this; - } - - // 所有数字类型都转换为 decimal - LSPAny& operator=(int val) - { - value = static_cast(val); - return *this; - } - - LSPAny& operator=(long val) - { - value = static_cast(val); - return *this; - } - - LSPAny& operator=(long long val) - { - value = static_cast(val); - return *this; - } - - LSPAny& operator=(unsigned int val) - { - value = static_cast(val); - return *this; - } - - LSPAny& operator=(unsigned long val) - { - value = static_cast(val); - return *this; - } - - LSPAny& operator=(unsigned long long val) - { - value = static_cast(val); - return *this; - } - - LSPAny& operator=(float val) - { - value = static_cast(val); - return *this; - } - - LSPAny& operator=(double val) - { - value = val; - return *this; - } - - LSPAny& operator=(long double val) - { - value = static_cast(val); - return *this; - } - - LSPAny& operator=(boolean val) - { - value = val; - return *this; - } - - LSPAny& operator=(std::nullptr_t) - { - value = nullptr; - return *this; - } - - // 类型检查辅助函数 - template - bool is() const - { - return std::holds_alternative(value); - } - - template - T& get() - { - return std::get(value); - } - - template - const T& get() const - { - return std::get(value); - } - - // 访问操作符 - template - auto visit(Visitor&& visitor) const - { - return std::visit(std::forward(visitor), value); - } - - template - auto visit(Visitor&& visitor) - { - return std::visit(std::forward(visitor), value); - } -}; - -// glaze 自动支持 std::variant,无需额外配置 -namespace glz -{ - template<> - struct meta - { - using T = LSPAny; - static constexpr auto value = &T::value; - }; -} - -// ===== 简单清晰的转换模板 ===== - -// 转换工具类 -struct LSPConvert -{ - // === 基本类型的转换(优先级最高) === - - // boolean - static LSPAny ToLSPAny(boolean value) - { - return LSPAny(value); - } - - // 整数类型 - static LSPAny ToLSPAny(int value) - { - return LSPAny(value); - } - - static LSPAny ToLSPAny(long value) - { - return LSPAny(value); - } - - static LSPAny ToLSPAny(long long value) - { - return LSPAny(value); - } - - static LSPAny ToLSPAny(unsigned int value) - { - return LSPAny(value); - } - - static LSPAny ToLSPAny(unsigned long value) - { - return LSPAny(value); - } - - static LSPAny ToLSPAny(unsigned long long value) - { - return LSPAny(value); - } - - // 浮点类型 - static LSPAny ToLSPAny(float value) - { - return LSPAny(value); - } - - static LSPAny ToLSPAny(double value) - { - return LSPAny(value); - } - - static LSPAny ToLSPAny(long double value) - { - return LSPAny(value); - } - - // string - static LSPAny ToLSPAny(const string& str) - { - return LSPAny(str); - } - - static LSPAny ToLSPAny(const char* str) - { - return LSPAny(str); - } - - // nullptr - static LSPAny ToLSPAny(std::nullptr_t) - { - return LSPAny(nullptr); - } - - // LSPAny 自身 - static LSPAny ToLSPAny(const LSPAny& any) - { - return any; - } - - // === 容器类型的转换 === - - // vector - template - static LSPAny ToLSPAny(const std::vector& vec) - { - LSPArray arr; - arr.reserve(vec.size()); - for (const auto& item : vec) - { - arr.push_back(ToLSPAny(item)); - } - return LSPAny(std::move(arr)); - } - - // map - template - static LSPAny ToLSPAny(const std::map& map) - { - LSPObject obj; - for (const auto& [key, value] : map) - { - obj[key] = ToLSPAny(value); - } - return LSPAny(std::move(obj)); - } - - // optional - template - static LSPAny ToLSPAny(const std::optional& opt) - { - if (opt.has_value()) - { - return ToLSPAny(*opt); - } - return LSPAny(nullptr); - } - - // === Struct 到 LSPAny 的转换(最低优先级) === - template - static typename std::enable_if< - !std::is_arithmetic::value && - !std::is_same::value && - !std::is_same::value && - !std::is_same::value && - !std::is_same::value, - LSPAny>::type - ToLSPAny(const T& obj) - { - // 序列化为 JSON 字符串 - std::string json; - auto ec = glz::write_json(obj, json); - if (ec) - { - throw std::runtime_error("Failed to serialize to JSON"); - } - - // 直接解析为 LSPAny - LSPAny result; - ec = glz::read_json(result, json); - if (ec) - { - throw std::runtime_error("Failed to parse JSON to LSPAny"); - } - - return result; - } - - // === LSPAny 到基本类型的转换 === - - // boolean - static boolean BoolFromLSPAny(const LSPAny& any) - { - if (!any.is()) - { - throw std::runtime_error("LSPAny is not a boolean"); - } - return any.get(); - } - - // 数字类型 - template - static typename std::enable_if::value && !std::is_same::value, T>::type - NumberFromLSPAny(const LSPAny& any) - { - if (!any.is()) - { - throw std::runtime_error("LSPAny is not a number"); - } - return static_cast(any.get()); - } - - // string - static string StringFromLSPAny(const LSPAny& any) - { - if (!any.is()) - { - throw std::runtime_error("LSPAny is not a string"); - } - return any.get(); - } - - // === LSPAny 到容器类型的转换 === - - // vector - template - static std::vector VectorFromLSPAny(const LSPAny& any) - { - if (!any.is()) - { - throw std::runtime_error("LSPAny is not an array"); - } - - const auto& arr = any.get(); - std::vector result; - result.reserve(arr.size()); - for (const auto& item : arr) - { - result.push_back(FromLSPAny(item)); - } - return result; - } - - // optional - template - static std::optional OptionalFromLSPAny(const LSPAny& any) - { - if (any.is()) - { - return std::nullopt; - } - return FromLSPAny(any); - } - - // === LSPAny 到 Struct 的转换 === - template - static T FromLSPAny(const LSPAny& any) - { - return FromLSPAnyImpl(any); - } - -private: - // 内部实现,使用重载来处理不同类型 - - // boolean - static boolean FromLSPAnyImpl(const LSPAny& any, boolean*) - { - return BoolFromLSPAny(any); - } - - // string - static string FromLSPAnyImpl(const LSPAny& any, string*) - { - return StringFromLSPAny(any); - } - - // 数字类型 - template - static typename std::enable_if::value && !std::is_same::value, T>::type - FromLSPAnyImpl(const LSPAny& any, T*) - { - return NumberFromLSPAny(any); - } - - // LSPAny 自身 - static LSPAny FromLSPAnyImpl(const LSPAny& any, LSPAny*) - { - return any; - } - - // struct 类型 - template - static typename std::enable_if< - !std::is_arithmetic::value && - !std::is_same::value && - !std::is_same::value, - T>::type - FromLSPAnyImpl(const LSPAny& any, T*) - { - // 序列化 LSPAny 为 JSON - std::string json; - auto ec = glz::write_json(any.value, json); - if (ec) - { - throw std::runtime_error("Failed to serialize LSPAny to JSON"); - } - - // 解析 JSON 到目标类型 - T result; - ec = glz::read_json(result, json); - if (ec) - { - throw std::runtime_error("Failed to parse JSON to target type"); - } - - return result; - } - - // 通过指针类型来分派 - template - static T FromLSPAnyImpl(const LSPAny& any) - { - return FromLSPAnyImpl(any, static_cast(nullptr)); - } -}; - -// ===== 便利函数(可选) ===== - -// 基本类型的便利函数 -inline LSPAny ToLSPAny(boolean value) { return LSPConvert::ToLSPAny(value); } -inline LSPAny ToLSPAny(int value) { return LSPConvert::ToLSPAny(value); } -inline LSPAny ToLSPAny(long value) { return LSPConvert::ToLSPAny(value); } -inline LSPAny ToLSPAny(long long value) { return LSPConvert::ToLSPAny(value); } -inline LSPAny ToLSPAny(unsigned int value) { return LSPConvert::ToLSPAny(value); } -inline LSPAny ToLSPAny(unsigned long value) { return LSPConvert::ToLSPAny(value); } -inline LSPAny ToLSPAny(unsigned long long value) { return LSPConvert::ToLSPAny(value); } -inline LSPAny ToLSPAny(float value) { return LSPConvert::ToLSPAny(value); } -inline LSPAny ToLSPAny(double value) { return LSPConvert::ToLSPAny(value); } -inline LSPAny ToLSPAny(long double value) { return LSPConvert::ToLSPAny(value); } -inline LSPAny ToLSPAny(const string& str) { return LSPConvert::ToLSPAny(str); } -inline LSPAny ToLSPAny(const char* str) { return LSPConvert::ToLSPAny(str); } -inline LSPAny ToLSPAny(std::nullptr_t) { return LSPConvert::ToLSPAny(nullptr); } -inline LSPAny ToLSPAny(const LSPAny& any) { return LSPConvert::ToLSPAny(any); } - -// 容器类型的便利函数 -template -LSPAny ToLSPAny(const std::vector& vec) -{ - return LSPConvert::ToLSPAny(vec); -} - -template -LSPAny ToLSPAny(const std::map& map) -{ - return LSPConvert::ToLSPAny(map); -} - -template -LSPAny ToLSPAny(const std::optional& opt) -{ - return LSPConvert::ToLSPAny(opt); -} - -// struct 类型的便利函数 -template -typename std::enable_if< - !std::is_arithmetic::value && - !std::is_same::value && - !std::is_same::value && - !std::is_same::value && - !std::is_same::value, - LSPAny>::type -ToLSPAny(const T& value) -{ - return LSPConvert::ToLSPAny(value); -} - -// FromLSPAny 便利函数 -template -T FromLSPAny(const LSPAny& any) -{ - return LSPConvert::FromLSPAny(any); -} - -// ===== 测试代码 ===== - -// 测试用的结构体 -struct Message -{ - string jsonrpc = "2.0"; - string method; - int id = 0; -}; - -struct Position -{ - int line; - int character; -}; - -struct Range -{ - Position start; - Position end; -}; - -struct TextDocumentIdentifier -{ - string uri; -}; - -struct TextDocumentPositionParams -{ - TextDocumentIdentifier textDocument; - Position position; -}; - -struct CompletionItem -{ - string label; - std::optional kind; - std::optional detail; - std::optional documentation; - std::optional deprecated; - std::optional preselect; - std::optional sortText; - std::optional filterText; - std::optional insertText; -}; - -// 为测试结构体提供 glaze 元数据 -template<> -struct glz::meta -{ - using T = Message; - static constexpr auto value = object( - "jsonrpc", - &T::jsonrpc, - "method", - &T::method, - "id", - &T::id); -}; - -template<> -struct glz::meta -{ - using T = Position; - static constexpr auto value = object( - "line", - &T::line, - "character", - &T::character); -}; - -template<> -struct glz::meta -{ - using T = Range; - static constexpr auto value = object( - "start", - &T::start, - "end", - &T::end); -}; - -template<> -struct glz::meta -{ - using T = TextDocumentIdentifier; - static constexpr auto value = object( - "uri", - &T::uri); -}; - -template<> -struct glz::meta -{ - using T = TextDocumentPositionParams; - static constexpr auto value = object( - "textDocument", - &T::textDocument, - "position", - &T::position); -}; - -template<> -struct glz::meta -{ - using T = CompletionItem; - static constexpr auto value = object( - "label", - &T::label, - "kind", - &T::kind, - "detail", - &T::detail, - "documentation", - &T::documentation, - "deprecated", - &T::deprecated, - "preselect", - &T::preselect, - "sortText", - &T::sortText, - "filterText", - &T::filterText, - "insertText", - &T::insertText); -}; - -// 测试函数 -#include - -void test_basic_conversion() -{ - std::cout << "=== Testing Basic Conversion ===" << std::endl; - - // 测试 struct 转换 - Message msg; - msg.method = "textDocument/completion"; - msg.id = 42; - - // Struct -> LSPAny - LSPAny any = ToLSPAny(msg); - - // 序列化查看 - std::string json; - glz::write_json(any, json); - std::cout << "Message as JSON: " << json << std::endl; - - // LSPAny -> Struct - Message restored = FromLSPAny(any); - std::cout << "Restored method: " << restored.method << std::endl; - std::cout << "Restored id: " << restored.id << std::endl; -} - -void test_vector_conversion() -{ - std::cout << "\n=== Testing Vector Conversion ===" << std::endl; - - std::vector positions = { - { 10, 5 }, - { 20, 15 }, - { 30, 25 } - }; - - // Vector -> LSPAny - LSPAny any = ToLSPAny(positions); - - std::string json; - glz::write_json(any, json); - std::cout << "Vector as JSON: " << json << std::endl; - - // LSPAny -> Vector - auto restored = LSPConvert::VectorFromLSPAny(any); - std::cout << "Restored " << restored.size() << " positions" << std::endl; -} - -void test_optional_conversion() -{ - std::cout << "\n=== Testing Optional Conversion ===" << std::endl; - - CompletionItem item; - item.label = "std::vector"; - item.kind = 1; - item.detail = "template class vector"; - - LSPAny any = ToLSPAny(item); - - std::string json; - glz::write_json(any, json); - std::cout << "CompletionItem as JSON: " << json << std::endl; - - // 测试 null optional - std::optional empty_opt; - LSPAny null_any = ToLSPAny(empty_opt); - glz::write_json(null_any, json); - std::cout << "Empty optional as JSON: " << json << std::endl; -} - -void test_mixed_usage() -{ - std::cout << "\n=== Testing Mixed Usage ===" << std::endl; - - // 创建复杂对象 - LSPObject obj; - obj["message"] = ToLSPAny(Message{ "2.0", "test/method", 123 }); - obj["position"] = ToLSPAny(Position{ 10, 20 }); - obj["numbers"] = ToLSPAny(std::vector{ 1, 2, 3 }); - obj["flag"] = LSPAny(true); - obj["value"] = LSPAny(3.14); - obj["text"] = LSPAny("Hello LSP"); - obj["empty"] = LSPAny(nullptr); - - LSPAny root(obj); - - std::string json; - glz::write_json(root, json); - std::cout << "Complex object: " << json << std::endl; - - // 提取值 - if (root.is()) - { - auto& root_obj = root.get(); - - Message msg = FromLSPAny(root_obj["message"]); - std::cout << "Extracted message method: " << msg.method << std::endl; - - Position pos = FromLSPAny(root_obj["position"]); - std::cout << "Extracted position: " << pos.line << ", " << pos.character << std::endl; - } -} - -int main() -{ - try - { - test_basic_conversion(); - test_vector_conversion(); - test_optional_conversion(); - test_mixed_usage(); - - std::cout << "\nAll tests completed!" << std::endl; - } - catch (const std::exception& e) - { - std::cout << "Exception: " << e.what() << std::endl; - return 1; - } - - return 0; -} diff --git a/lsp-server/test/test_lsp_any/CMakeLists.txt b/lsp-server/test/test_lsp_any/CMakeLists.txt new file mode 100644 index 0000000..19c4e46 --- /dev/null +++ b/lsp-server/test/test_lsp_any/CMakeLists.txt @@ -0,0 +1,72 @@ +cmake_minimum_required(VERSION 4.0) + +project(test_lsp_any) + +set(CMAKE_CXX_STANDARD 23) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +message(STATUS "CMAKE_CXX_COMPILER_ID: ${CMAKE_CXX_COMPILER_ID}") +message(STATUS "CMAKE_SYSTEM_NAME: ${CMAKE_SYSTEM_NAME}") +message(STATUS "CMAKE_BUILD_TYPE: ${CMAKE_BUILD_TYPE}") + +if (DEFINED CMAKE_TOOLCHAIN_FILE) + message(STATUS ">>> CMAKE_TOOLCHAIN_FILE: ${CMAKE_TOOLCHAIN_FILE}") +endif() +if (DEFINED VCPKG_TARGET_TRIPLET) + message(STATUS ">>> VCPKG_TARGET_TRIPLET: ${VCPKG_TARGET_TRIPLET}") +endif() + +# 设置默认构建类型 +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE + "Release" + CACHE STRING "Build type" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" + "MinSizeRel" "RelWithDebInfo") +endif() + +# MinGW/MSYS2 静态链接 +if(MINGW) + add_link_options(-static -static-libgcc -static-libstdc++) +elseif(UNIX AND NOT APPLE) # Linux 静态链接 + add_link_options(-static-libgcc -static-libstdc++) +endif() + +if(WIN32) + set(CMAKE_FIND_LIBRARY_SUFFIXES ".a" ".lib" ".dll.a") +else() + set(CMAKE_FIND_LIBRARY_SUFFIXES ".a" ".so") +endif() + +find_package(glaze CONFIG REQUIRED) + +if(UNIX AND NOT APPLE) + find_package(Threads REQUIRED) +endif() + +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src) +set(SOURCES + ./common_test.cpp + ./facade_test.cpp + ./lsp_any_test.cpp + ./test_framework.cpp + ./test_main.cpp + ./transformer_test.cpp +) + +add_executable(${PROJECT_NAME} ${SOURCES}) +target_include_directories(${PROJECT_NAME} PRIVATE src) + +target_link_libraries(${PROJECT_NAME} PRIVATE + glaze::glaze + $<$:Threads::Threads> # 使用生成器表达式 +) + +if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") + target_compile_options(${PROJECT_NAME} PRIVATE + -Wall -Wextra -Wpedantic + $<$:-g -O0> + $<$:-O3> + ) +endif() + diff --git a/lsp-server/test/test_lsp_any/common_test.cpp b/lsp-server/test/test_lsp_any/common_test.cpp new file mode 100644 index 0000000..4c467ec --- /dev/null +++ b/lsp-server/test/test_lsp_any/common_test.cpp @@ -0,0 +1,325 @@ +#include "common_test.hpp" +#include +#include +#include +#include +#include "../../src/protocol/transform/common.hpp" +#include "../../src/protocol/detail/basic_types.hpp" + +namespace lsp::test +{ + // 用于测试的用户自定义结构体 + struct UserStruct + { + int value; + std::string name; + }; + + void CommonTests::registerTests(TestRunner& runner) + { + // is_lsp_basic_type 测试 + runner.addTest("Common - is_lsp_basic_type (integer)", testIsLSPBasicInteger); + runner.addTest("Common - is_lsp_basic_type (uinteger)", testIsLSPBasicUInteger); + runner.addTest("Common - is_lsp_basic_type (boolean)", testIsLSPBasicBoolean); + runner.addTest("Common - is_lsp_basic_type (string)", testIsLSPBasicString); + runner.addTest("Common - is_lsp_basic_type (decimal)", testIsLSPBasicDecimal); + runner.addTest("Common - is_lsp_basic_type (nullptr_t)", testIsLSPBasicNullptr); + runner.addTest("Common - is_lsp_basic_type (负面案例)", testIsLSPBasicNegativeCases); + + // is_lsp_container_type 测试 + runner.addTest("Common - is_lsp_container_type (LSPObject)", testIsLSPContainerObject); + runner.addTest("Common - is_lsp_container_type (LSPArray)", testIsLSPContainerArray); + runner.addTest("Common - is_lsp_container_type (LSPAny)", testIsLSPContainerAny); + runner.addTest("Common - is_lsp_container_type (负面案例)", testIsLSPContainerNegativeCases); + + // is_user_struct 测试 + runner.addTest("Common - is_user_struct (用户结构体)", testIsUserStructPositive); + runner.addTest("Common - is_user_struct (排除算术类型)", testIsUserStructExcludeArithmetic); + runner.addTest("Common - is_user_struct (排除字符串)", testIsUserStructExcludeString); + runner.addTest("Common - is_user_struct (排除LSP类型)", testIsUserStructExcludeLSPTypes); + runner.addTest("Common - is_user_struct (排除vector)", testIsUserStructExcludeVector); + runner.addTest("Common - is_user_struct (排除map)", testIsUserStructExcludeMap); + runner.addTest("Common - is_user_struct (排除optional)", testIsUserStructExcludeOptional); + runner.addTest("Common - is_user_struct (排除variant)", testIsUserStructExcludeVariant); + runner.addTest("Common - is_user_struct (排除指针)", testIsUserStructExcludePointers); + } + + // ==================== is_lsp_basic_type 测试 ==================== + + TestResult CommonTests::testIsLSPBasicInteger() + { + TestResult result; + result.passed = true; + + bool is_basic = transform::is_lsp_basic_type_v; + assertTrue(is_basic, "integer应该是LSP基本类型"); + + result.message = "成功"; + return result; + } + + TestResult CommonTests::testIsLSPBasicUInteger() + { + TestResult result; + result.passed = true; + + bool is_basic = transform::is_lsp_basic_type_v; + assertTrue(is_basic, "uinteger应该是LSP基本类型"); + + result.message = "成功"; + return result; + } + + TestResult CommonTests::testIsLSPBasicBoolean() + { + TestResult result; + result.passed = true; + + bool is_basic = transform::is_lsp_basic_type_v; + assertTrue(is_basic, "boolean应该是LSP基本类型"); + + result.message = "成功"; + return result; + } + + TestResult CommonTests::testIsLSPBasicString() + { + TestResult result; + result.passed = true; + + bool is_basic = transform::is_lsp_basic_type_v; + assertTrue(is_basic, "string应该是LSP基本类型"); + + result.message = "成功"; + return result; + } + + TestResult CommonTests::testIsLSPBasicDecimal() + { + TestResult result; + result.passed = true; + + bool is_basic = transform::is_lsp_basic_type_v; + assertTrue(is_basic, "decimal应该是LSP基本类型"); + + result.message = "成功"; + return result; + } + + TestResult CommonTests::testIsLSPBasicNullptr() + { + TestResult result; + result.passed = true; + + bool is_basic = transform::is_lsp_basic_type_v; + assertTrue(is_basic, "nullptr_t应该是LSP基本类型"); + + result.message = "成功"; + return result; + } + + TestResult CommonTests::testIsLSPBasicNegativeCases() + { + TestResult result; + result.passed = true; + + // 测试明确不是 LSP 基本类型的类型 + // 注意:避免测试 int/int32_t,因为它们可能等于 protocol::integer + assertFalse(transform::is_lsp_basic_type_v, + "int64_t不应该是LSP基本类型"); + assertFalse(transform::is_lsp_basic_type_v, + "long long不应该是LSP基本类型"); + assertFalse(transform::is_lsp_basic_type_v, + "float不应该是LSP基本类型"); + assertFalse(transform::is_lsp_basic_type_v>, + "vector不应该是LSP基本类型"); + assertFalse(transform::is_lsp_basic_type_v, + "UserStruct不应该是LSP基本类型"); + + result.message = "成功"; + return result; + } + + // ==================== is_lsp_container_type 测试 ==================== + + TestResult CommonTests::testIsLSPContainerObject() + { + TestResult result; + result.passed = true; + + bool is_container = transform::is_lsp_container_type_v; + assertTrue(is_container, "LSPObject应该是LSP容器类型"); + + result.message = "成功"; + return result; + } + + TestResult CommonTests::testIsLSPContainerArray() + { + TestResult result; + result.passed = true; + + bool is_container = transform::is_lsp_container_type_v; + assertTrue(is_container, "LSPArray应该是LSP容器类型"); + + result.message = "成功"; + return result; + } + + TestResult CommonTests::testIsLSPContainerAny() + { + TestResult result; + result.passed = true; + + bool is_container = transform::is_lsp_container_type_v; + assertTrue(is_container, "LSPAny应该是LSP容器类型"); + + result.message = "成功"; + return result; + } + + TestResult CommonTests::testIsLSPContainerNegativeCases() + { + TestResult result; + result.passed = true; + + assertFalse(transform::is_lsp_container_type_v, + "int不应该是LSP容器"); + assertFalse(transform::is_lsp_container_type_v, + "string不应该是LSP容器"); + assertFalse(transform::is_lsp_container_type_v>, + "std::vector不应该是LSP容器"); + + result.message = "成功"; + return result; + } + + // ==================== is_user_struct 测试 ==================== + + TestResult CommonTests::testIsUserStructPositive() + { + TestResult result; + result.passed = true; + + bool is_user = transform::is_user_struct_v; + assertTrue(is_user, "UserStruct应该被识别为用户结构体"); + + result.message = "成功"; + return result; + } + + TestResult CommonTests::testIsUserStructExcludeArithmetic() + { + TestResult result; + result.passed = true; + + assertFalse(transform::is_user_struct_v, + "int不应该是用户结构体"); + assertFalse(transform::is_user_struct_v, + "double不应该是用户结构体"); + assertFalse(transform::is_user_struct_v, + "bool不应该是用户结构体"); + assertFalse(transform::is_user_struct_v, + "char不应该是用户结构体"); + + result.message = "成功"; + return result; + } + + TestResult CommonTests::testIsUserStructExcludeString() + { + TestResult result; + result.passed = true; + + assertFalse(transform::is_user_struct_v, + "std::string不应该是用户结构体"); + assertFalse(transform::is_user_struct_v, + "protocol::string不应该是用户结构体"); + + result.message = "成功"; + return result; + } + + TestResult CommonTests::testIsUserStructExcludeLSPTypes() + { + TestResult result; + result.passed = true; + + assertFalse(transform::is_user_struct_v, + "LSPObject不应该是用户结构体"); + assertFalse(transform::is_user_struct_v, + "LSPArray不应该是用户结构体"); + assertFalse(transform::is_user_struct_v, + "LSPAny不应该是用户结构体"); + assertFalse(transform::is_user_struct_v, + "protocol::integer不应该是用户结构体"); + + result.message = "成功"; + return result; + } + + TestResult CommonTests::testIsUserStructExcludeVector() + { + TestResult result; + result.passed = true; + + bool is_user = transform::is_user_struct_v>; + assertFalse(is_user, "std::vector不应该是用户结构体"); + + result.message = "成功"; + return result; + } + + TestResult CommonTests::testIsUserStructExcludeMap() + { + TestResult result; + result.passed = true; + + bool is_user = transform::is_user_struct_v>; + assertFalse(is_user, "std::map不应该是用户结构体"); + + result.message = "成功"; + return result; + } + + TestResult CommonTests::testIsUserStructExcludeOptional() + { + TestResult result; + result.passed = true; + + bool is_user = transform::is_user_struct_v>; + assertFalse(is_user, "std::optional不应该是用户结构体"); + + result.message = "成功"; + return result; + } + + TestResult CommonTests::testIsUserStructExcludeVariant() + { + TestResult result; + result.passed = true; + + bool is_user = transform::is_user_struct_v>; + assertFalse(is_user, "std::variant不应该是用户结构体"); + + result.message = "成功"; + return result; + } + + TestResult CommonTests::testIsUserStructExcludePointers() + { + TestResult result; + result.passed = true; + + assertFalse(transform::is_user_struct_v, + "int*不应该是用户结构体"); + assertFalse(transform::is_user_struct_v, + "const char*不应该是用户结构体"); + assertFalse(transform::is_user_struct_v, + "UserStruct*不应该是用户结构体"); + + result.message = "成功"; + return result; + } + +} // namespace lsp::test diff --git a/lsp-server/test/test_lsp_any/common_test.hpp b/lsp-server/test/test_lsp_any/common_test.hpp new file mode 100644 index 0000000..9ccc8a7 --- /dev/null +++ b/lsp-server/test/test_lsp_any/common_test.hpp @@ -0,0 +1,39 @@ +#pragma once +#include "test_framework.hpp" + +namespace lsp::test +{ + // Common 类型特征测试类 + class CommonTests + { + public: + static void registerTests(TestRunner& runner); + + private: + // ==================== is_lsp_basic_type 测试 ==================== + static TestResult testIsLSPBasicInteger(); + static TestResult testIsLSPBasicUInteger(); + static TestResult testIsLSPBasicBoolean(); + static TestResult testIsLSPBasicString(); + static TestResult testIsLSPBasicDecimal(); + static TestResult testIsLSPBasicNullptr(); + static TestResult testIsLSPBasicNegativeCases(); + + // ==================== is_lsp_container_type 测试 ==================== + static TestResult testIsLSPContainerObject(); + static TestResult testIsLSPContainerArray(); + static TestResult testIsLSPContainerAny(); + static TestResult testIsLSPContainerNegativeCases(); + + // ==================== is_user_struct 测试 ==================== + static TestResult testIsUserStructPositive(); + static TestResult testIsUserStructExcludeArithmetic(); + static TestResult testIsUserStructExcludeString(); + static TestResult testIsUserStructExcludeLSPTypes(); + static TestResult testIsUserStructExcludeVector(); + static TestResult testIsUserStructExcludeMap(); + static TestResult testIsUserStructExcludeOptional(); + static TestResult testIsUserStructExcludeVariant(); + static TestResult testIsUserStructExcludePointers(); + }; +} diff --git a/lsp-server/test/test_lsp_any/facade_test.cpp b/lsp-server/test/test_lsp_any/facade_test.cpp new file mode 100644 index 0000000..96dd5a2 --- /dev/null +++ b/lsp-server/test/test_lsp_any/facade_test.cpp @@ -0,0 +1,389 @@ +#include +#include "facade_test.hpp" +#include "../../src/protocol/transform/facade.hpp" +#include "../../src/protocol/detail/basic_types.hpp" + +namespace lsp::test +{ + // 测试用结构体 + struct TestPerson + { + std::string name; + int age; + }; + + struct TestConfig + { + std::string version; + bool enabled; + std::vector ports; + }; +} + +// Glaze 元数据 +template<> +struct glz::meta +{ + using T = lsp::test::TestPerson; + static constexpr auto value = object( + "name", &T::name, + "age", &T::age + ); +}; + +template<> +struct glz::meta +{ + using T = lsp::test::TestConfig; + static constexpr auto value = object( + "version", &T::version, + "enabled", &T::enabled, + "ports", &T::ports + ); +}; + +namespace lsp::test +{ + void FacadeTests::registerTests(TestRunner& runner) + { + // Serialize/Deserialize 测试 + runner.addTest("Facade - 序列化简单结构体", testSerializeSimpleStruct); + runner.addTest("Facade - 序列化复杂结构体", testSerializeComplexStruct); + runner.addTest("Facade - 序列化LSPAny", testSerializeLSPAny); + runner.addTest("Facade - 反序列化简单结构体", testDeserializeSimpleStruct); + runner.addTest("Facade - 反序列化复杂结构体", testDeserializeComplexStruct); + runner.addTest("Facade - 反序列化无效JSON", testDeserializeInvalidJSON); + runner.addTest("Facade - 序列化反序列化往返", testSerializeDeserializeRoundtrip); + + // check 命名空间测试 + runner.addTest("Facade - Check IsObject", testCheckIsObject); + runner.addTest("Facade - Check IsArray", testCheckIsArray); + runner.addTest("Facade - Check IsString", testCheckIsString); + runner.addTest("Facade - Check IsNumber", testCheckIsNumber); + runner.addTest("Facade - Check IsBool", testCheckIsBool); + runner.addTest("Facade - Check IsNull", testCheckIsNull); + runner.addTest("Facade - Check 多类型检查", testCheckMultipleTypes); + + // debug 命名空间测试 + runner.addTest("Facade - Debug GetTypeName", testDebugGetTypeName); + runner.addTest("Facade - Debug GetIdString (int)", testDebugGetIdStringInt); + runner.addTest("Facade - Debug GetIdString (string)", testDebugGetIdStringString); + } + + // ==================== Serialize/Deserialize 测试 ==================== + + TestResult FacadeTests::testSerializeSimpleStruct() + { + TestResult result; + result.passed = true; + + TestPerson person{"Alice", 30}; + auto json_opt = transform::Serialize(person); + + assertTrue(json_opt.has_value(), "序列化应该成功"); + const auto& json = json_opt.value(); + assertTrue(json.find("Alice") != std::string::npos, "JSON应该包含'Alice'"); + assertTrue(json.find("30") != std::string::npos, "JSON应该包含'30'"); + + result.message = "成功"; + return result; + } + + TestResult FacadeTests::testSerializeComplexStruct() + { + TestResult result; + result.passed = true; + + TestConfig config{"1.0.0", true, {8080, 8081, 8082}}; + auto json_opt = transform::Serialize(config); + + assertTrue(json_opt.has_value(), "序列化应该成功"); + const auto& json = json_opt.value(); + assertTrue(json.find("1.0.0") != std::string::npos, "JSON应该包含版本号"); + assertTrue(json.find("true") != std::string::npos, "JSON应该包含enabled"); + assertTrue(json.find("8080") != std::string::npos, "JSON应该包含端口"); + + result.message = "成功"; + return result; + } + + TestResult FacadeTests::testSerializeLSPAny() + { + TestResult result; + result.passed = true; + + protocol::LSPObject obj; + obj["key"] = protocol::LSPAny(42); + obj["name"] = protocol::LSPAny("test"); + protocol::LSPAny any(obj); + + auto json_opt = transform::Serialize(any); + assertTrue(json_opt.has_value(), "序列化LSPAny应该成功"); + + result.message = "成功"; + return result; + } + + TestResult FacadeTests::testDeserializeSimpleStruct() + { + TestResult result; + result.passed = true; + + std::string json = R"({"name":"Bob","age":25})"; + auto person_opt = transform::Deserialize(json); + + assertTrue(person_opt.has_value(), "反序列化应该成功"); + const auto& person = person_opt.value(); + assertEqual(std::string("Bob"), person.name, "名字应该为'Bob'"); + assertEqual(25, person.age, "年龄应该为25"); + + result.message = "成功"; + return result; + } + + TestResult FacadeTests::testDeserializeComplexStruct() + { + TestResult result; + result.passed = true; + + std::string json = R"({"version":"2.0.0","enabled":false,"ports":[9000,9001,9002]})"; + auto config_opt = transform::Deserialize(json); + + assertTrue(config_opt.has_value(), "反序列化应该成功"); + const auto& config = config_opt.value(); + assertEqual(std::string("2.0.0"), config.version, "版本应该为'2.0.0'"); + assertEqual(false, config.enabled, "enabled应该为false"); + assertEqual(size_t(3), config.ports.size(), "应该有3个端口"); + assertEqual(9000, config.ports[0], "第一个端口应该为9000"); + + result.message = "成功"; + return result; + } + + TestResult FacadeTests::testDeserializeInvalidJSON() + { + TestResult result; + result.passed = true; + + std::string json = R"({"invalid json syntax)"; + auto person_opt = transform::Deserialize(json); + + assertFalse(person_opt.has_value(), "反序列化无效JSON应该失败"); + + result.message = "成功"; + return result; + } + + TestResult FacadeTests::testSerializeDeserializeRoundtrip() + { + TestResult result; + result.passed = true; + + TestPerson original{"Charlie", 35}; + + auto json_opt = transform::Serialize(original); + assertTrue(json_opt.has_value(), "序列化应该成功"); + + auto person_opt = transform::Deserialize(json_opt.value()); + assertTrue(person_opt.has_value(), "反序列化应该成功"); + + const auto& restored = person_opt.value(); + assertEqual(original.name, restored.name, "名字应该保持一致"); + assertEqual(original.age, restored.age, "年龄应该保持一致"); + + result.message = "成功"; + return result; + } + + // ==================== check 命名空间测试 ==================== + + TestResult FacadeTests::testCheckIsObject() + { + TestResult result; + result.passed = true; + + protocol::LSPObject obj; + obj["key"] = protocol::LSPAny(1); + protocol::LSPAny any(obj); + + assertTrue(transform::check::IsObject(any), "应该识别为object"); + + protocol::LSPAny not_obj(42); + assertFalse(transform::check::IsObject(not_obj), "integer不应该识别为object"); + + result.message = "成功"; + return result; + } + + TestResult FacadeTests::testCheckIsArray() + { + TestResult result; + result.passed = true; + + protocol::LSPArray arr; + arr.push_back(protocol::LSPAny(1)); + protocol::LSPAny any(arr); + + assertTrue(transform::check::IsArray(any), "应该识别为array"); + + protocol::LSPAny not_arr("string"); + assertFalse(transform::check::IsArray(not_arr), "string不应该识别为array"); + + result.message = "成功"; + return result; + } + + TestResult FacadeTests::testCheckIsString() + { + TestResult result; + result.passed = true; + + protocol::LSPAny any("hello"); + assertTrue(transform::check::IsString(any), "应该识别为string"); + + protocol::LSPAny not_str(42); + assertFalse(transform::check::IsString(not_str), "integer不应该识别为string"); + + result.message = "成功"; + return result; + } + + TestResult FacadeTests::testCheckIsNumber() + { + TestResult result; + result.passed = true; + + protocol::LSPAny int_any(42); + assertTrue(transform::check::IsNumber(int_any), "integer应该是数字"); + + protocol::LSPAny uint_any(100u); + assertTrue(transform::check::IsNumber(uint_any), "uinteger应该是数字"); + + protocol::LSPAny decimal_any(3.14); + assertTrue(transform::check::IsNumber(decimal_any), "decimal应该是数字"); + + protocol::LSPAny not_num("string"); + assertFalse(transform::check::IsNumber(not_num), "string不应该是数字"); + + result.message = "成功"; + return result; + } + + TestResult FacadeTests::testCheckIsBool() + { + TestResult result; + result.passed = true; + + protocol::LSPAny any(true); + assertTrue(transform::check::IsBool(any), "应该识别为bool"); + + protocol::LSPAny not_bool(42); + assertFalse(transform::check::IsBool(not_bool), "integer不应该识别为bool"); + + result.message = "成功"; + return result; + } + + TestResult FacadeTests::testCheckIsNull() + { + TestResult result; + result.passed = true; + + protocol::LSPAny any(nullptr); + assertTrue(transform::check::IsNull(any), "应该识别为null"); + + protocol::LSPAny not_null(42); + assertFalse(transform::check::IsNull(not_null), "integer不应该识别为null"); + + result.message = "成功"; + return result; + } + + TestResult FacadeTests::testCheckMultipleTypes() + { + TestResult result; + result.passed = true; + + // 测试一个值不能同时是多种类型 + protocol::LSPAny any(42); + assertTrue(transform::check::IsNumber(any), "应该是数字"); + assertFalse(transform::check::IsString(any), "不应该是字符串"); + assertFalse(transform::check::IsBool(any), "不应该是布尔值"); + assertFalse(transform::check::IsNull(any), "不应该是null"); + assertFalse(transform::check::IsArray(any), "不应该是数组"); + assertFalse(transform::check::IsObject(any), "不应该是对象"); + + result.message = "成功"; + return result; + } + + // ==================== debug 命名空间测试 ==================== + + TestResult FacadeTests::testDebugGetTypeName() + { + TestResult result; + result.passed = true; + + protocol::LSPAny obj_any(protocol::LSPObject{}); + assertEqual(std::string("LSPObject"), transform::debug::GetTypeName(obj_any), + "类型名应该为'LSPObject'"); + + protocol::LSPAny arr_any(protocol::LSPArray{}); + assertEqual(std::string("LSPArray"), transform::debug::GetTypeName(arr_any), + "类型名应该为'LSPArray'"); + + protocol::LSPAny str_any("test"); + assertEqual(std::string("string"), transform::debug::GetTypeName(str_any), + "类型名应该为'string'"); + + protocol::LSPAny int_any(42); + assertEqual(std::string("integer"), transform::debug::GetTypeName(int_any), + "类型名应该为'integer'"); + + protocol::LSPAny uint_any(100u); + assertEqual(std::string("uinteger"), transform::debug::GetTypeName(uint_any), + "类型名应该为'uinteger'"); + + protocol::LSPAny decimal_any(3.14); + assertEqual(std::string("decimal"), transform::debug::GetTypeName(decimal_any), + "类型名应该为'decimal'"); + + protocol::LSPAny bool_any(true); + assertEqual(std::string("boolean"), transform::debug::GetTypeName(bool_any), + "类型名应该为'boolean'"); + + protocol::LSPAny null_any(nullptr); + assertEqual(std::string("null"), transform::debug::GetTypeName(null_any), + "类型名应该为'null'"); + + result.message = "成功"; + return result; + } + + TestResult FacadeTests::testDebugGetIdStringInt() + { + TestResult result; + result.passed = true; + + std::variant id = 123; + std::string id_str = transform::debug::GetIdString(id); + assertEqual(std::string("123"), id_str, "int id应该转换为'123'"); + + result.message = "成功"; + return result; + } + + TestResult FacadeTests::testDebugGetIdStringString() + { + TestResult result; + result.passed = true; + + std::variant id = std::string("abc-123-def"); + std::string id_str = transform::debug::GetIdString(id); + assertEqual(std::string("abc-123-def"), id_str, "string id应该保持为'abc-123-def'"); + + result.message = "成功"; + return result; + } + +} // namespace lsp::test diff --git a/lsp-server/test/test_lsp_any/facade_test.hpp b/lsp-server/test/test_lsp_any/facade_test.hpp new file mode 100644 index 0000000..b77f884 --- /dev/null +++ b/lsp-server/test/test_lsp_any/facade_test.hpp @@ -0,0 +1,36 @@ +#pragma once +#include "test_framework.hpp" + +namespace lsp::test +{ + // Facade 接口测试类(Serialize, Deserialize, check, debug) + class FacadeTests + { + public: + static void registerTests(TestRunner& runner); + + private: + // ==================== Serialize/Deserialize 测试 ==================== + static TestResult testSerializeSimpleStruct(); + static TestResult testSerializeComplexStruct(); + static TestResult testSerializeLSPAny(); + static TestResult testDeserializeSimpleStruct(); + static TestResult testDeserializeComplexStruct(); + static TestResult testDeserializeInvalidJSON(); + static TestResult testSerializeDeserializeRoundtrip(); + + // ==================== check 命名空间测试 ==================== + static TestResult testCheckIsObject(); + static TestResult testCheckIsArray(); + static TestResult testCheckIsString(); + static TestResult testCheckIsNumber(); + static TestResult testCheckIsBool(); + static TestResult testCheckIsNull(); + static TestResult testCheckMultipleTypes(); + + // ==================== debug 命名空间测试 ==================== + static TestResult testDebugGetTypeName(); + static TestResult testDebugGetIdStringInt(); + static TestResult testDebugGetIdStringString(); + }; +} diff --git a/lsp-server/test/test_lsp_any/lsp_any_test.cpp b/lsp-server/test/test_lsp_any/lsp_any_test.cpp new file mode 100644 index 0000000..f98de88 --- /dev/null +++ b/lsp-server/test/test_lsp_any/lsp_any_test.cpp @@ -0,0 +1,689 @@ +#include +#include +#include "../../src/protocol/detail/basic_types.hpp" +#include "./lsp_any_test.hpp" + +namespace lsp::test +{ + + // ==================== LSPAnyTests 实现 ==================== + + void LSPAnyTests::registerTests(TestRunner& runner) + { + // 构造函数测试 + runner.addTest("默认构造函数", testDefaultConstructor); + runner.addTest("浮点数构造函数", testDecimalConstructor); + runner.addTest("布尔构造函数", testBooleanConstructor); + runner.addTest("字符串构造函数", testStringConstructor); + runner.addTest("C字符串构造函数", testCStringConstructor); + runner.addTest("空指针构造函数", testNullptrConstructor); + runner.addTest("LSPObject构造函数", testLSPObjectConstructor); + runner.addTest("LSPArray构造函数", testLSPArrayConstructor); + + // 拷贝和移动测试 + runner.addTest("拷贝构造函数", testCopyConstructor); + runner.addTest("移动构造函数", testMoveConstructor); + runner.addTest("拷贝赋值", testCopyAssignment); + runner.addTest("移动赋值", testMoveAssignment); + + // 赋值操作符测试 + runner.addTest("浮点数赋值", testDecimalAssignment); + runner.addTest("布尔赋值", testBooleanAssignment); + runner.addTest("字符串赋值", testStringAssignment); + runner.addTest("空指针赋值", testNullptrAssignment); + runner.addTest("LSPObject赋值", testLSPObjectAssignment); + runner.addTest("LSPArray赋值", testLSPArrayAssignment); + + // 类型检查测试 + runner.addTest("Is类型检查", testIsMethod); + runner.addTest("Get获取值", testGetMethod); + + // 访问者模式测试 + runner.addTest("Visit访问者", testVisitMethod); + + // 整数模板测试 + runner.addTest("有符号整数模板", testIntegerTemplateSignedTypes); + runner.addTest("无符号整数模板", testIntegerTemplateUnsignedTypes); + runner.addTest("整数边界测试", testIntegerTemplateBoundaries); + + // 复杂场景测试 + runner.addTest("嵌套LSPObject", testNestedLSPObject); + runner.addTest("嵌套LSPArray", testNestedLSPArray); + runner.addTest("混合嵌套", testMixedNesting); + runner.addTest("类型转换", testTypeConversion); + + // 边界情况测试 + runner.addTest("浮点精度", testFloatPrecision); + runner.addTest("大数字", testLargeNumbers); + runner.addTest("空容器", testEmptyContainers); + } + + // ==================== 构造函数测试 ==================== + + TestResult LSPAnyTests::testDefaultConstructor() + { + TestResult result; + result.passed = true; + + protocol::LSPAny any; + assertTrue(any.Is(), "默认构造应该是nullptr类型"); + + result.message = "成功"; + return result; + } + + TestResult LSPAnyTests::testDecimalConstructor() + { + TestResult result; + result.passed = true; + + protocol::decimal value = 3.14; + protocol::LSPAny any(value); + + assertTrue(any.Is(), "应该是decimal类型"); + assertEqual(value, any.Get(), "值应该相等"); + + // 测试 float 构造 + float fValue = 2.5f; + protocol::LSPAny anyFloat(fValue); + assertTrue(anyFloat.Is(), "float应该转为decimal类型"); + + result.message = "成功"; + return result; + } + + TestResult LSPAnyTests::testBooleanConstructor() + { + TestResult result; + result.passed = true; + + protocol::LSPAny anyTrue(true); + protocol::LSPAny anyFalse(false); + + assertTrue(anyTrue.Is(), "应该是boolean类型"); + assertTrue(anyFalse.Is(), "应该是boolean类型"); + assertEqual(true, anyTrue.Get(), "值应该是true"); + assertEqual(false, anyFalse.Get(), "值应该是false"); + + result.message = "成功"; + return result; + } + + TestResult LSPAnyTests::testStringConstructor() + { + TestResult result; + result.passed = true; + + protocol::string value = "Hello, LSP!"; + protocol::LSPAny any(value); + + assertTrue(any.Is(), "应该是string类型"); + assertEqual(value, any.Get(), "值应该相等"); + + result.message = "成功"; + return result; + } + + TestResult LSPAnyTests::testCStringConstructor() + { + TestResult result; + result.passed = true; + + const char* value = "C String"; + protocol::LSPAny any(value); + + assertTrue(any.Is(), "应该是string类型"); + assertEqual(std::string(value), any.Get(), "值应该相等"); + + result.message = "成功"; + return result; + } + + TestResult LSPAnyTests::testNullptrConstructor() + { + TestResult result; + result.passed = true; + + protocol::LSPAny any(nullptr); + + assertTrue(any.Is(), "应该是nullptr类型"); + + result.message = "成功"; + return result; + } + + TestResult LSPAnyTests::testLSPObjectConstructor() + { + TestResult result; + result.passed = true; + + protocol::LSPObject obj; + obj["key1"] = protocol::LSPAny(42); + obj["key2"] = protocol::LSPAny("value"); + + protocol::LSPAny any(obj); + + assertTrue(any.Is(), "应该是LSPObject类型"); + const auto& retrievedObj = any.Get(); + assertTrue(retrievedObj.count("key1") > 0, "应该包含key1"); + assertTrue(retrievedObj.count("key2") > 0, "应该包含key2"); + + result.message = "成功"; + return result; + } + + TestResult LSPAnyTests::testLSPArrayConstructor() + { + TestResult result; + result.passed = true; + + protocol::LSPArray arr; + arr.push_back(protocol::LSPAny(1)); + arr.push_back(protocol::LSPAny(2)); + arr.push_back(protocol::LSPAny(3)); + + protocol::LSPAny any(arr); + + assertTrue(any.Is(), "应该是LSPArray类型"); + const auto& retrievedArr = any.Get(); + assertTrue(retrievedArr.size() == 3, "数组大小应该是3"); + + result.message = "成功"; + return result; + } + + // ==================== 拷贝和移动测试 ==================== + + TestResult LSPAnyTests::testCopyConstructor() + { + TestResult result; + result.passed = true; + + protocol::LSPAny original(42); + protocol::LSPAny copy(original); + + assertTrue(copy.Is(), "拷贝应该保持类型"); + assertEqual(42, copy.Get(), "拷贝应该保持值"); + assertEqual(42, original.Get(), "原值不应改变"); + + result.message = "成功"; + return result; + } + + TestResult LSPAnyTests::testMoveConstructor() + { + TestResult result; + result.passed = true; + + protocol::LSPAny original(protocol::string("Move me")); + protocol::LSPAny moved(std::move(original)); + + assertTrue(moved.Is(), "移动应该保持类型"); + assertEqual(std::string("Move me"), moved.Get(), "移动应该保持值"); + + result.message = "成功"; + return result; + } + + TestResult LSPAnyTests::testCopyAssignment() + { + TestResult result; + result.passed = true; + + protocol::LSPAny original(42); + protocol::LSPAny target(nullptr); + target = original; + + assertTrue(target.Is(), "赋值应该保持类型"); + assertEqual(42, target.Get(), "赋值应该保持值"); + + result.message = "成功"; + return result; + } + + TestResult LSPAnyTests::testMoveAssignment() + { + TestResult result; + result.passed = true; + + protocol::LSPAny original(protocol::string("Move me")); + protocol::LSPAny target(nullptr); + target = std::move(original); + + assertTrue(target.Is(), "移动赋值应该保持类型"); + assertEqual(std::string("Move me"), target.Get(), "移动赋值应该保持值"); + + result.message = "成功"; + return result; + } + + // ==================== 赋值操作符测试 ==================== + + TestResult LSPAnyTests::testDecimalAssignment() + { + TestResult result; + result.passed = true; + + protocol::LSPAny any(nullptr); + any = 2.718; + + assertTrue(any.Is(), "赋值后应该是decimal类型"); + assertEqual(2.718, any.Get(), "赋值后值应该正确"); + + result.message = "成功"; + return result; + } + + TestResult LSPAnyTests::testBooleanAssignment() + { + TestResult result; + result.passed = true; + + protocol::LSPAny any(nullptr); + any = true; + + assertTrue(any.Is(), "赋值后应该是boolean类型"); + assertEqual(true, any.Get(), "赋值后值应该正确"); + + result.message = "成功"; + return result; + } + + TestResult LSPAnyTests::testStringAssignment() + { + TestResult result; + result.passed = true; + + protocol::LSPAny any(nullptr); + any = protocol::string("Assigned"); + + assertTrue(any.Is(), "赋值后应该是string类型"); + assertEqual(std::string("Assigned"), any.Get(), "赋值后值应该正确"); + + // 测试 C 字符串赋值 + any = "C String"; + assertEqual(std::string("C String"), any.Get(), "C字符串赋值应该正确"); + + result.message = "成功"; + return result; + } + + TestResult LSPAnyTests::testNullptrAssignment() + { + TestResult result; + result.passed = true; + + protocol::LSPAny any(42); + any = nullptr; + + assertTrue(any.Is(), "赋值后应该是nullptr类型"); + + result.message = "成功"; + return result; + } + + TestResult LSPAnyTests::testLSPObjectAssignment() + { + TestResult result; + result.passed = true; + + protocol::LSPObject obj; + obj["test"] = protocol::LSPAny(999); + + protocol::LSPAny any(nullptr); + any = obj; + + assertTrue(any.Is(), "赋值后应该是LSPObject类型"); + const auto& retrievedObj = any.Get(); + assertTrue(retrievedObj.count("test") > 0, "应该包含test键"); + + result.message = "成功"; + return result; + } + + TestResult LSPAnyTests::testLSPArrayAssignment() + { + TestResult result; + result.passed = true; + + protocol::LSPArray arr; + arr.push_back(protocol::LSPAny(10)); + arr.push_back(protocol::LSPAny(20)); + + protocol::LSPAny any(nullptr); + any = arr; + + assertTrue(any.Is(), "赋值后应该是LSPArray类型"); + const auto& retrievedArr = any.Get(); + assertTrue(retrievedArr.size() == 2, "数组大小应该是2"); + + result.message = "成功"; + return result; + } + + // ==================== 类型检查测试 ==================== + + TestResult LSPAnyTests::testIsMethod() + { + TestResult result; + result.passed = true; + + protocol::LSPAny intAny(42); + assertTrue(intAny.Is(), "Is应该正确识别integer"); + assertFalse(intAny.Is(), "Is应该正确排除string"); + assertFalse(intAny.Is(), "Is应该正确排除decimal"); + + protocol::LSPAny strAny(protocol::string("test")); + assertTrue(strAny.Is(), "Is应该正确识别string"); + assertFalse(strAny.Is(), "Is应该正确排除integer"); + + result.message = "成功"; + return result; + } + + TestResult LSPAnyTests::testGetMethod() + { + TestResult result; + result.passed = true; + + protocol::LSPAny any(42); + assertEqual(42, any.Get(), "Get应该返回正确的值"); + + // 修改值 + any.Get() = 100; + assertEqual(100, any.Get(), "Get应该返回可修改的引用"); + + // const Get 测试 + const protocol::LSPAny constAny(200); + assertEqual(200, constAny.Get(), "const Get应该正常工作"); + + result.message = "成功"; + return result; + } + + // ==================== 访问者模式测试 ==================== + + TestResult LSPAnyTests::testVisitMethod() + { + TestResult result; + result.passed = true; + + protocol::LSPAny intAny(42); + protocol::LSPAny strAny(protocol::string("hello")); + protocol::LSPAny boolAny(true); + + // 测试 const Visit + auto visitor = [](const auto& value) -> std::string { + using T = std::decay_t; + if constexpr (std::is_same_v) + { + return "integer:" + std::to_string(value); + } + else if constexpr (std::is_same_v) + { + return "string:" + value; + } + else if constexpr (std::is_same_v) + { + return value ? "bool:true" : "bool:false"; + } + else + { + return "other"; + } + }; + + std::string result1 = intAny.Visit(visitor); + assertEqual(std::string("integer:42"), result1, "Visit应该正确访问integer"); + + std::string result2 = strAny.Visit(visitor); + assertEqual(std::string("string:hello"), result2, "Visit应该正确访问string"); + + std::string result3 = boolAny.Visit(visitor); + assertEqual(std::string("bool:true"), result3, "Visit应该正确访问boolean"); + + result.message = "成功"; + return result; + } + + // ==================== 整数模板测试 ==================== + + TestResult LSPAnyTests::testIntegerTemplateSignedTypes() + { + TestResult result; + result.passed = true; + + // 测试各种有符号整数类型 + protocol::LSPAny anyInt8(static_cast(-10)); + assertTrue(anyInt8.Is(), "int8_t应该转为integer"); + assertEqual(-10, anyInt8.Get(), "int8_t值应该正确"); + + protocol::LSPAny anyInt16(static_cast(-1000)); + assertTrue(anyInt16.Is(), "int16_t应该转为integer"); + assertEqual(-1000, anyInt16.Get(), "int16_t值应该正确"); + + protocol::LSPAny anyInt64(static_cast(100000)); + assertTrue(anyInt64.Is(), "int64_t应该转为integer"); + assertEqual(100000, anyInt64.Get(), "int64_t值应该正确"); + + result.message = "成功"; + return result; + } + + TestResult LSPAnyTests::testIntegerTemplateUnsignedTypes() + { + TestResult result; + result.passed = true; + + // 测试各种无符号整数类型 - 无符号整数应该存储为uinteger + protocol::LSPAny anyUint8(static_cast(255)); + assertTrue(anyUint8.Is(), "uint8_t应该转为uinteger"); + assertEqual(255u, anyUint8.Get(), "uint8_t值应该正确"); + + protocol::LSPAny anyUint16(static_cast(5000)); + assertTrue(anyUint16.Is(), "uint16_t应该转为uinteger"); + assertEqual(5000u, anyUint16.Get(), "uint16_t值应该正确"); + + protocol::LSPAny anySize(static_cast(12345)); + assertTrue(anySize.Is(), "size_t应该转为uinteger"); + assertEqual(12345u, anySize.Get(), "size_t值应该正确"); + + result.message = "成功"; + return result; + } + + TestResult LSPAnyTests::testIntegerTemplateBoundaries() + { + TestResult result; + result.passed = true; + + // 测试 int32 边界 + protocol::LSPAny anyMax(std::numeric_limits::max()); + assertTrue(anyMax.Is(), "int32_max应该是integer"); + assertEqual(std::numeric_limits::max(), anyMax.Get(), "int32_max值应该正确"); + + protocol::LSPAny anyMin(std::numeric_limits::min()); + assertTrue(anyMin.Is(), "int32_min应该是integer"); + assertEqual(std::numeric_limits::min(), anyMin.Get(), "int32_min值应该正确"); + + // 测试超出 int32 范围的 int64 + int64_t largeValue = static_cast(std::numeric_limits::max()) + 1; + protocol::LSPAny anyLarge(largeValue); + assertTrue(anyLarge.Is(), "超出int32范围应该转为decimal"); + + // 测试 uint32 边界 + protocol::LSPAny anyUintMax(std::numeric_limits::max()); + assertTrue(anyUintMax.Is(), "uint32_max应该是uinteger"); + assertEqual(std::numeric_limits::max(), anyUintMax.Get(), "uint32_max值应该正确"); + + result.message = "成功"; + return result; + } + + // ==================== 复杂场景测试 ==================== + + TestResult LSPAnyTests::testNestedLSPObject() + { + TestResult result; + result.passed = true; + + protocol::LSPObject innerObj; + innerObj["inner_key"] = protocol::LSPAny(42); + + protocol::LSPObject outerObj; + outerObj["outer_key"] = protocol::LSPAny(innerObj); + outerObj["name"] = protocol::LSPAny(protocol::string("test")); + + protocol::LSPAny any(outerObj); + + assertTrue(any.Is(), "应该是LSPObject类型"); + const auto& obj = any.Get(); + assertTrue(obj.count("outer_key") > 0, "应该包含outer_key"); + assertTrue(obj.count("name") > 0, "应该包含name"); + + const auto& inner = obj.at("outer_key"); + assertTrue(inner.Is(), "嵌套值应该是LSPObject"); + + result.message = "成功"; + return result; + } + + TestResult LSPAnyTests::testNestedLSPArray() + { + TestResult result; + result.passed = true; + + protocol::LSPArray innerArr; + innerArr.push_back(protocol::LSPAny(1)); + innerArr.push_back(protocol::LSPAny(2)); + + protocol::LSPArray outerArr; + outerArr.push_back(protocol::LSPAny(innerArr)); + outerArr.push_back(protocol::LSPAny(protocol::string("text"))); + + protocol::LSPAny any(outerArr); + + assertTrue(any.Is(), "应该是LSPArray类型"); + const auto& arr = any.Get(); + assertTrue(arr.size() == 2, "外层数组大小应该是2"); + assertTrue(arr[0].Is(), "第一个元素应该是LSPArray"); + + result.message = "成功"; + return result; + } + + TestResult LSPAnyTests::testMixedNesting() + { + TestResult result; + result.passed = true; + + protocol::LSPArray arr; + arr.push_back(protocol::LSPAny(1)); + arr.push_back(protocol::LSPAny(protocol::string("two"))); + arr.push_back(protocol::LSPAny(3.0)); + + protocol::LSPObject obj; + obj["array"] = protocol::LSPAny(arr); + obj["number"] = protocol::LSPAny(42); + obj["flag"] = protocol::LSPAny(true); + + protocol::LSPAny any(obj); + + assertTrue(any.Is(), "应该是LSPObject类型"); + const auto& retrievedObj = any.Get(); + assertTrue(retrievedObj.count("array") > 0, "应该包含array"); + assertTrue(retrievedObj.at("array").Is(), "array应该是LSPArray类型"); + + const auto& retrievedArr = retrievedObj.at("array").Get(); + assertTrue(retrievedArr.size() == 3, "数组大小应该是3"); + + result.message = "成功"; + return result; + } + + TestResult LSPAnyTests::testTypeConversion() + { + TestResult result; + result.passed = true; + + protocol::LSPAny any(42); + assertTrue(any.Is(), "初始应该是integer"); + + // 改变类型 + any = protocol::string("now a string"); + assertTrue(any.Is(), "现在应该是string"); + assertFalse(any.Is(), "不应该再是integer"); + + // 再次改变 + any = 3.14; + assertTrue(any.Is(), "现在应该是decimal"); + assertFalse(any.Is(), "不应该再是string"); + + result.message = "成功"; + return result; + } + + // ==================== 边界情况测试 ==================== + + TestResult LSPAnyTests::testFloatPrecision() + { + TestResult result; + result.passed = true; + + float fValue = 1.23456789f; + protocol::LSPAny anyFloat(fValue); + + assertTrue(anyFloat.Is(), "float应该转为decimal"); + + // 注意:float 转为 double 可能有精度变化,这里只是验证类型转换正确 + protocol::decimal retrieved = anyFloat.Get(); + assertTrue(std::abs(retrieved - static_cast(fValue)) < 1e-6, + "转换后的值应该接近原值"); + + result.message = "成功"; + return result; + } + + TestResult LSPAnyTests::testLargeNumbers() + { + TestResult result; + result.passed = true; + + // 测试大的 uint64 值转为 decimal + uint64_t largeUint = 10000000000ULL; // 超出 uint32 范围 + protocol::LSPAny anyLarge(largeUint); + + assertTrue(anyLarge.Is(), "大整数应该转为decimal"); + + protocol::decimal retrieved = anyLarge.Get(); + assertTrue(std::abs(retrieved - static_cast(largeUint)) < 1.0, + "转换后的值应该接近原值"); + + result.message = "成功"; + return result; + } + + TestResult LSPAnyTests::testEmptyContainers() + { + TestResult result; + result.passed = true; + + protocol::LSPObject emptyObj; + protocol::LSPAny anyObj(emptyObj); + + assertTrue(anyObj.Is(), "应该是LSPObject类型"); + const auto& obj = anyObj.Get(); + assertTrue(obj.empty(), "对象应该为空"); + + protocol::LSPArray emptyArr; + protocol::LSPAny anyArr(emptyArr); + + assertTrue(anyArr.Is(), "应该是LSPArray类型"); + const auto& arr = anyArr.Get(); + assertTrue(arr.empty(), "数组应该为空"); + + result.message = "成功"; + return result; + } + +} // namespace lsp::test diff --git a/lsp-server/test/test_lsp_any/lsp_any_test.hpp b/lsp-server/test/test_lsp_any/lsp_any_test.hpp new file mode 100644 index 0000000..8167b72 --- /dev/null +++ b/lsp-server/test/test_lsp_any/lsp_any_test.hpp @@ -0,0 +1,62 @@ +#pragma once +#include "./test_framework.hpp" + +namespace lsp::test +{ + // LSPAny 测试类 + class LSPAnyTests + { + public: + static void registerTests(TestRunner& runner); + + private: + // 构造函数测试 + static TestResult testDefaultConstructor(); + static TestResult testDecimalConstructor(); + static TestResult testBooleanConstructor(); + static TestResult testStringConstructor(); + static TestResult testCStringConstructor(); + static TestResult testNullptrConstructor(); + static TestResult testLSPObjectConstructor(); + static TestResult testLSPArrayConstructor(); + + // 拷贝和移动测试 + static TestResult testCopyConstructor(); + static TestResult testMoveConstructor(); + static TestResult testCopyAssignment(); + static TestResult testMoveAssignment(); + + // 赋值操作符测试 + static TestResult testIntegerAssignment(); + static TestResult testUIntegerAssignment(); + static TestResult testDecimalAssignment(); + static TestResult testBooleanAssignment(); + static TestResult testStringAssignment(); + static TestResult testNullptrAssignment(); + static TestResult testLSPObjectAssignment(); + static TestResult testLSPArrayAssignment(); + + // 类型检查测试 + static TestResult testIsMethod(); + static TestResult testGetMethod(); + + // 访问者模式测试 + static TestResult testVisitMethod(); + + // 整数模板测试 + static TestResult testIntegerTemplateSignedTypes(); + static TestResult testIntegerTemplateUnsignedTypes(); + static TestResult testIntegerTemplateBoundaries(); + + // 复杂场景测试 + static TestResult testNestedLSPObject(); + static TestResult testNestedLSPArray(); + static TestResult testMixedNesting(); + static TestResult testTypeConversion(); + + // 边界情况测试 + static TestResult testFloatPrecision(); + static TestResult testLargeNumbers(); + static TestResult testEmptyContainers(); + }; +} diff --git a/lsp-server/test/test_lsp_any/test_framework.cpp b/lsp-server/test/test_lsp_any/test_framework.cpp new file mode 100644 index 0000000..ecd1775 --- /dev/null +++ b/lsp-server/test/test_lsp_any/test_framework.cpp @@ -0,0 +1,178 @@ +#include +#include +#include +#include +#include "./test_framework.hpp" + +namespace lsp::test +{ + // ==================== TestRunner 实现 ==================== + + void TestRunner::addTest(const std::string& name, TestFunction test) + { + tests.push_back({ name, test }); + } + + void TestRunner::runAllTests() + { + results.clear(); + std::cout << "\n========== 开始运行测试 ==========\n" + << std::endl; + + for (const auto& testCase : tests) + { + try + { + TestResult result = testCase.function(); + result.testName = testCase.name; + results.push_back(result); + printResult(result); + } + catch (const std::exception& e) + { + TestResult result; + result.testName = testCase.name; + result.passed = false; + result.message = std::string("异常: ") + e.what(); + results.push_back(result); + printResult(result); + } + } + + printSummary(); + } + + int TestRunner::getFailedCount() const + { + int count = 0; + for (const auto& result : results) + { + if (!result.passed) + { + count++; + } + } + return count; + } + + int TestRunner::getTotalCount() const + { + return static_cast(results.size()); + } + + void TestRunner::printResult(const TestResult& result) + { + if (result.passed) + { + std::cout << "[✓] " << result.testName << std::endl; + } + else + { + std::cout << "[✗] " << result.testName << std::endl; + std::cout << " 失败: " << result.message << std::endl; + } + } + + void TestRunner::printSummary() + { + int passed = getTotalCount() - getFailedCount(); + int failed = getFailedCount(); + int total = getTotalCount(); + + std::cout << "\n========== 测试总结 ==========\n"; + std::cout << "总计: " << total << " 个测试\n"; + std::cout << "通过: " << passed << " 个\n"; + std::cout << "失败: " << failed << " 个\n"; + + if (failed == 0) + { + std::cout << "\n所有测试通过! ✓\n" + << std::endl; + } + else + { + std::cout << "\n部分测试失败 ✗\n" + << std::endl; + } + } + + // ==================== 断言辅助函数实现 ==================== + + void assertTrue(bool condition, const std::string& message) + { + if (!condition) + { + throw std::runtime_error(message); + } + } + + void assertFalse(bool condition, const std::string& message) + { + if (condition) + { + throw std::runtime_error(message); + } + } + + void assertEqual(int expected, int actual, const std::string& message) + { + if (expected != actual) + { + std::stringstream ss; + ss << message << " (期望: " << expected << ", 实际: " << actual << ")"; + throw std::runtime_error(ss.str()); + } + } + + void assertEqual(unsigned int expected, unsigned int actual, const std::string& message) + { + if (expected != actual) + { + std::stringstream ss; + ss << message << " (期望: " << expected << ", 实际: " << actual << ")"; + throw std::runtime_error(ss.str()); + } + } + + void assertEqual(double expected, double actual, const std::string& message) + { + if (std::abs(expected - actual) > 1e-9) + { + std::stringstream ss; + ss << message << " (期望: " << expected << ", 实际: " << actual << ")"; + throw std::runtime_error(ss.str()); + } + } + + void assertEqual(bool expected, bool actual, const std::string& message) + { + if (expected != actual) + { + std::stringstream ss; + ss << message << " (期望: " << (expected ? "true" : "false") + << ", 实际: " << (actual ? "true" : "false") << ")"; + throw std::runtime_error(ss.str()); + } + } + + void assertEqual(const std::string& expected, const std::string& actual, const std::string& message) + { + if (expected != actual) + { + std::stringstream ss; + ss << message << " (期望: \"" << expected << "\", 实际: \"" << actual << "\")"; + throw std::runtime_error(ss.str()); + } + } + + void assertEqual(size_t expected, size_t actual, const std::string& message) + { + if (expected != actual) + { + std::stringstream ss; + ss << message << " (期望: " << expected << ", 实际: " << actual << ")"; + throw std::runtime_error(ss.str()); + } + } + +} // namespace lsp::test diff --git a/lsp-server/test/test_lsp_any/test_framework.hpp b/lsp-server/test/test_lsp_any/test_framework.hpp new file mode 100644 index 0000000..0d5a418 --- /dev/null +++ b/lsp-server/test/test_lsp_any/test_framework.hpp @@ -0,0 +1,50 @@ +#pragma once +#include +#include +#include + +namespace lsp::test +{ + // 简单的测试结果结构 + struct TestResult + { + std::string testName; + bool passed; + std::string message; + }; + + // 测试运行器类 + class TestRunner + { + public: + using TestFunction = std::function; + + void addTest(const std::string& name, TestFunction test); + void runAllTests(); + int getFailedCount() const; + int getTotalCount() const; + + private: + struct TestCase + { + std::string name; + TestFunction function; + }; + + std::vector tests; + std::vector results; + + void printResult(const TestResult& result); + void printSummary(); + }; + + // 断言辅助函数 + void assertTrue(bool condition, const std::string& message); + void assertFalse(bool condition, const std::string& message); + void assertEqual(int expected, int actual, const std::string& message); + void assertEqual(unsigned int expected, unsigned int actual, const std::string& message); + void assertEqual(double expected, double actual, const std::string& message); + void assertEqual(bool expected, bool actual, const std::string& message); + void assertEqual(const std::string& expected, const std::string& actual, const std::string& message); + void assertEqual(size_t expected, size_t actual, const std::string& message); +} diff --git a/lsp-server/test/test_lsp_any/test_main.cpp b/lsp-server/test/test_lsp_any/test_main.cpp new file mode 100644 index 0000000..0f8c9ff --- /dev/null +++ b/lsp-server/test/test_lsp_any/test_main.cpp @@ -0,0 +1,36 @@ +#include +#include "test_framework.hpp" +#include "lsp_any_test.hpp" +#include "transformer_test.hpp" +#include "facade_test.hpp" +#include "common_test.hpp" + +int main() +{ + lsp::test::TestRunner runner; + + std::cout << "\n========================================" << std::endl; + std::cout << " LSP Transform 库单元测试套件" << std::endl; + std::cout << "========================================\n" << std::endl; + + // 注册所有测试 + std::cout << "正在注册测试..." << std::endl; + + std::cout << " - LSPAny 基本功能测试" << std::endl; + lsp::test::LSPAnyTests::registerTests(runner); + + std::cout << " - Transformer 转换功能测试" << std::endl; + lsp::test::TransformerTests::registerTests(runner); + + std::cout << " - Facade 接口测试" << std::endl; + lsp::test::FacadeTests::registerTests(runner); + + std::cout << " - Common 类型特征测试" << std::endl; + lsp::test::CommonTests::registerTests(runner); + + // 运行所有测试 + runner.runAllTests(); + + // 返回失败的测试数量(用于CI/CD) + return runner.getFailedCount(); +} diff --git a/lsp-server/test/test_lsp_any/transformer_test.cpp b/lsp-server/test/test_lsp_any/transformer_test.cpp new file mode 100644 index 0000000..d6a7e2c --- /dev/null +++ b/lsp-server/test/test_lsp_any/transformer_test.cpp @@ -0,0 +1,654 @@ +#include "transformer_test.hpp" +#include "../../src/protocol/transform/transformer.hpp" +#include "../../src/protocol/transform/common.hpp" +#include "../../src/protocol/detail/basic_types.hpp" +#include +#include +#include + +namespace lsp::test +{ + void TransformerTests::registerTests(TestRunner& runner) + { + // ToLSPAny 基本类型 + runner.addTest("Transformer - ToLSPAny Boolean", testToLSPAnyBoolean); + runner.addTest("Transformer - ToLSPAny Integer", testToLSPAnyInteger); + runner.addTest("Transformer - ToLSPAny UInteger", testToLSPAnyUInteger); + runner.addTest("Transformer - ToLSPAny Float", testToLSPAnyFloat); + runner.addTest("Transformer - ToLSPAny Double", testToLSPAnyDouble); + runner.addTest("Transformer - ToLSPAny String", testToLSPAnyString); + runner.addTest("Transformer - ToLSPAny CString", testToLSPAnyCString); + + // ToLSPAny 容器类型 + runner.addTest("Transformer - ToLSPAny Vector", testToLSPAnyVector); + runner.addTest("Transformer - ToLSPAny Map", testToLSPAnyMap); + runner.addTest("Transformer - ToLSPAny Optional (有值)", testToLSPAnyOptionalValue); + runner.addTest("Transformer - ToLSPAny Optional (nullopt)", testToLSPAnyOptionalNullopt); + + // ToLSPAny LSP类型 + runner.addTest("Transformer - ToLSPAny LSPObject", testToLSPAnyLSPObject); + runner.addTest("Transformer - ToLSPAny LSPArray", testToLSPAnyLSPArray); + runner.addTest("Transformer - ToLSPAny LSPAny", testToLSPAnyLSPAny); + + // FromLSPAny 基本类型 + runner.addTest("Transformer - FromLSPAny Boolean", testFromLSPAnyBoolean); + runner.addTest("Transformer - FromLSPAny Integer", testFromLSPAnyInteger); + runner.addTest("Transformer - FromLSPAny String", testFromLSPAnyString); + runner.addTest("Transformer - FromLSPAny Double", testFromLSPAnyDouble); + + // FromLSPAny 容器类型 + runner.addTest("Transformer - FromLSPAny Vector", testFromLSPAnyVector); + runner.addTest("Transformer - FromLSPAny Optional (有值)", testFromLSPAnyOptionalValue); + runner.addTest("Transformer - FromLSPAny Optional (null)", testFromLSPAnyOptionalNull); + runner.addTest("Transformer - FromLSPAny LSPObject", testFromLSPAnyLSPObject); + runner.addTest("Transformer - FromLSPAny LSPArray", testFromLSPAnyLSPArray); + + // 数字类型转换 + runner.addTest("Transformer - 提取Integer", testExtractNumberFromInteger); + runner.addTest("Transformer - 提取UInteger", testExtractNumberFromUInteger); + runner.addTest("Transformer - 提取Decimal", testExtractNumberFromDecimal); + runner.addTest("Transformer - 跨类型数字提取", testExtractNumberCrossType); + + // 错误处理 + runner.addTest("Transformer - 类型不匹配错误", testFromLSPAnyTypeMismatch); + runner.addTest("Transformer - 无效数字错误", testFromLSPAnyInvalidNumber); + runner.addTest("Transformer - 无效数组错误", testFromLSPAnyInvalidArray); + + // 嵌套结构 + runner.addTest("Transformer - 嵌套Vector", testNestedVector); + runner.addTest("Transformer - 嵌套LSPObject", testNestedLSPObject); + runner.addTest("Transformer - 混合类型嵌套", testMixedTypeNesting); + } + + // ==================== ToLSPAny 基本类型测试 ==================== + + TestResult TransformerTests::testToLSPAnyBoolean() + { + TestResult result; + result.passed = true; + + auto any = transform::LSPAnyConverter::ToLSPAny(true); + assertTrue(any.Is(), "应该转换为boolean类型"); + assertEqual(true, any.Get(), "值应该为true"); + + auto any_false = transform::LSPAnyConverter::ToLSPAny(false); + assertEqual(false, any_false.Get(), "值应该为false"); + + result.message = "成功"; + return result; + } + + TestResult TransformerTests::testToLSPAnyInteger() + { + TestResult result; + result.passed = true; + + auto any = transform::LSPAnyConverter::ToLSPAny(42); + assertTrue(any.Is(), "应该转换为integer类型"); + assertEqual(42, any.Get(), "值应该为42"); + + auto any_negative = transform::LSPAnyConverter::ToLSPAny(-100); + assertEqual(-100, any_negative.Get(), "负数应该正确转换"); + + result.message = "成功"; + return result; + } + + TestResult TransformerTests::testToLSPAnyUInteger() + { + TestResult result; + result.passed = true; + + auto any = transform::LSPAnyConverter::ToLSPAny(42u); + assertTrue(any.Is(), "应该转换为uinteger类型"); + assertEqual(42u, any.Get(), "值应该为42"); + + result.message = "成功"; + return result; + } + + TestResult TransformerTests::testToLSPAnyFloat() + { + TestResult result; + result.passed = true; + + auto any = transform::LSPAnyConverter::ToLSPAny(3.14f); + assertTrue(any.Is(), "float应该转换为decimal类型"); + + result.message = "成功"; + return result; + } + + TestResult TransformerTests::testToLSPAnyDouble() + { + TestResult result; + result.passed = true; + + auto any = transform::LSPAnyConverter::ToLSPAny(2.718); + assertTrue(any.Is(), "应该转换为decimal类型"); + assertEqual(2.718, any.Get(), "值应该为2.718"); + + result.message = "成功"; + return result; + } + + TestResult TransformerTests::testToLSPAnyString() + { + TestResult result; + result.passed = true; + + std::string str = "hello world"; + auto any = transform::LSPAnyConverter::ToLSPAny(str); + assertTrue(any.Is(), "应该转换为string类型"); + assertEqual(str, any.Get(), "值应该为'hello world'"); + + result.message = "成功"; + return result; + } + + TestResult TransformerTests::testToLSPAnyCString() + { + TestResult result; + result.passed = true; + + auto any = transform::LSPAnyConverter::ToLSPAny("test"); + assertTrue(any.Is(), "C字符串应该转换为string类型"); + assertEqual(std::string("test"), any.Get(), "值应该为'test'"); + + result.message = "成功"; + return result; + } + + // ==================== ToLSPAny 容器类型测试 ==================== + + TestResult TransformerTests::testToLSPAnyVector() + { + TestResult result; + result.passed = true; + + std::vector vec = { 1, 2, 3, 4, 5 }; + auto any = transform::LSPAnyConverter::ToLSPAny(vec); + + assertTrue(any.Is(), "应该转换为LSPArray类型"); + const auto& arr = any.Get(); + assertEqual(size_t(5), arr.size(), "数组大小应该为5"); + assertTrue(arr[0].Is(), "元素应该是integer类型"); + assertEqual(1, arr[0].Get(), "第一个元素应该为1"); + assertEqual(5, arr[4].Get(), "最后一个元素应该为5"); + + result.message = "成功"; + return result; + } + + TestResult TransformerTests::testToLSPAnyMap() + { + TestResult result; + result.passed = true; + + std::map map = { { "a", 1 }, { "b", 2 }, { "c", 3 } }; + auto any = transform::LSPAnyConverter::ToLSPAny(map); + + assertTrue(any.Is(), "应该转换为LSPObject类型"); + const auto& obj = any.Get(); + assertEqual(size_t(3), obj.size(), "对象大小应该为3"); + assertTrue(obj.count("a") > 0, "应该包含键'a'"); + assertEqual(1, obj.at("a").Get(), "键'a'的值应该为1"); + + result.message = "成功"; + return result; + } + + TestResult TransformerTests::testToLSPAnyOptionalValue() + { + TestResult result; + result.passed = true; + + std::optional opt = 100; + auto any = transform::LSPAnyConverter::ToLSPAny(opt); + + assertTrue(any.Is(), "有值的optional应该转换为对应类型"); + assertEqual(100, any.Get(), "值应该为100"); + + result.message = "成功"; + return result; + } + + TestResult TransformerTests::testToLSPAnyOptionalNullopt() + { + TestResult result; + result.passed = true; + + std::optional opt = std::nullopt; + auto any = transform::LSPAnyConverter::ToLSPAny(opt); + + assertTrue(any.Is(), "nullopt应该转换为null"); + + result.message = "成功"; + return result; + } + + // ==================== ToLSPAny LSP类型测试 ==================== + + TestResult TransformerTests::testToLSPAnyLSPObject() + { + TestResult result; + result.passed = true; + + protocol::LSPObject obj; + obj["key"] = protocol::LSPAny(42); + auto any = transform::LSPAnyConverter::ToLSPAny(obj); + + assertTrue(any.Is(), "应该保持LSPObject类型"); + const auto& retrieved = any.Get(); + assertEqual(size_t(1), retrieved.size(), "对象应该包含1个元素"); + + result.message = "成功"; + return result; + } + + TestResult TransformerTests::testToLSPAnyLSPArray() + { + TestResult result; + result.passed = true; + + protocol::LSPArray arr; + arr.push_back(protocol::LSPAny(1)); + arr.push_back(protocol::LSPAny(2)); + auto any = transform::LSPAnyConverter::ToLSPAny(arr); + + assertTrue(any.Is(), "应该保持LSPArray类型"); + const auto& retrieved = any.Get(); + assertEqual(size_t(2), retrieved.size(), "数组应该包含2个元素"); + + result.message = "成功"; + return result; + } + + TestResult TransformerTests::testToLSPAnyLSPAny() + { + TestResult result; + result.passed = true; + + protocol::LSPAny original(42); + auto any = transform::LSPAnyConverter::ToLSPAny(original); + + assertTrue(any.Is(), "应该保持类型"); + assertEqual(42, any.Get(), "应该保持值"); + + result.message = "成功"; + return result; + } + + // ==================== FromLSPAny 基本类型测试 ==================== + + TestResult TransformerTests::testFromLSPAnyBoolean() + { + TestResult result; + result.passed = true; + + protocol::LSPAny any(true); + bool value = transform::LSPAnyConverter::FromLSPAny(any); + assertEqual(true, value, "应该提取出true"); + + protocol::LSPAny any_false(false); + bool value_false = transform::LSPAnyConverter::FromLSPAny(any_false); + assertEqual(false, value_false, "应该提取出false"); + + result.message = "成功"; + return result; + } + + TestResult TransformerTests::testFromLSPAnyInteger() + { + TestResult result; + result.passed = true; + + protocol::LSPAny any(42); + int value = transform::LSPAnyConverter::FromLSPAny(any); + assertEqual(42, value, "应该提取出42"); + + result.message = "成功"; + return result; + } + + TestResult TransformerTests::testFromLSPAnyString() + { + TestResult result; + result.passed = true; + + protocol::LSPAny any("test string"); + std::string value = transform::LSPAnyConverter::FromLSPAny(any); + assertEqual(std::string("test string"), value, "应该提取出'test string'"); + + result.message = "成功"; + return result; + } + + TestResult TransformerTests::testFromLSPAnyDouble() + { + TestResult result; + result.passed = true; + + protocol::LSPAny any(3.14159); + double value = transform::LSPAnyConverter::FromLSPAny(any); + assertEqual(3.14159, value, "应该提取出3.14159"); + + result.message = "成功"; + return result; + } + + // ==================== FromLSPAny 容器类型测试 ==================== + + TestResult TransformerTests::testFromLSPAnyVector() + { + TestResult result; + result.passed = true; + + protocol::LSPArray arr; + arr.push_back(protocol::LSPAny(10)); + arr.push_back(protocol::LSPAny(20)); + arr.push_back(protocol::LSPAny(30)); + protocol::LSPAny any(arr); + + auto vec = transform::LSPAnyConverter::FromLSPAny>(any); + assertEqual(size_t(3), vec.size(), "vector大小应该为3"); + assertEqual(10, vec[0], "第一个元素应该为10"); + assertEqual(20, vec[1], "第二个元素应该为20"); + assertEqual(30, vec[2], "第三个元素应该为30"); + + result.message = "成功"; + return result; + } + + TestResult TransformerTests::testFromLSPAnyOptionalValue() + { + TestResult result; + result.passed = true; + + protocol::LSPAny any(42); + auto opt = transform::LSPAnyConverter::FromLSPAny>(any); + + assertTrue(opt.has_value(), "optional应该有值"); + assertEqual(42, opt.value(), "值应该为42"); + + result.message = "成功"; + return result; + } + + TestResult TransformerTests::testFromLSPAnyOptionalNull() + { + TestResult result; + result.passed = true; + + protocol::LSPAny any(nullptr); + auto opt = transform::LSPAnyConverter::FromLSPAny>(any); + + assertFalse(opt.has_value(), "optional应该为nullopt"); + + result.message = "成功"; + return result; + } + + TestResult TransformerTests::testFromLSPAnyLSPObject() + { + TestResult result; + result.passed = true; + + protocol::LSPObject obj; + obj["key1"] = protocol::LSPAny(100); + obj["key2"] = protocol::LSPAny("value"); + protocol::LSPAny any(obj); + + auto retrieved = transform::LSPAnyConverter::FromLSPAny(any); + assertEqual(size_t(2), retrieved.size(), "对象应该有2个键"); + assertTrue(retrieved.count("key1") > 0, "应该包含key1"); + assertTrue(retrieved.count("key2") > 0, "应该包含key2"); + + result.message = "成功"; + return result; + } + + TestResult TransformerTests::testFromLSPAnyLSPArray() + { + TestResult result; + result.passed = true; + + protocol::LSPArray arr; + arr.push_back(protocol::LSPAny(1)); + arr.push_back(protocol::LSPAny(2)); + protocol::LSPAny any(arr); + + auto retrieved = transform::LSPAnyConverter::FromLSPAny(any); + assertEqual(size_t(2), retrieved.size(), "数组应该有2个元素"); + + result.message = "成功"; + return result; + } + + // ==================== 数字类型转换测试 ==================== + + TestResult TransformerTests::testExtractNumberFromInteger() + { + TestResult result; + result.passed = true; + + protocol::LSPAny any(static_cast(42)); + int value = transform::LSPAnyConverter::FromLSPAny(any); + assertEqual(42, value, "应该从integer提取出42"); + + result.message = "成功"; + return result; + } + + TestResult TransformerTests::testExtractNumberFromUInteger() + { + TestResult result; + result.passed = true; + + protocol::LSPAny any(static_cast(100)); + unsigned int value = transform::LSPAnyConverter::FromLSPAny(any); + assertEqual(100u, value, "应该从uinteger提取出100"); + + result.message = "成功"; + return result; + } + + TestResult TransformerTests::testExtractNumberFromDecimal() + { + TestResult result; + result.passed = true; + + protocol::LSPAny any(static_cast(3.14)); + double value = transform::LSPAnyConverter::FromLSPAny(any); + assertEqual(3.14, value, "应该从decimal提取出3.14"); + + result.message = "成功"; + return result; + } + + TestResult TransformerTests::testExtractNumberCrossType() + { + TestResult result; + result.passed = true; + + // integer -> double + protocol::LSPAny int_any(42); + double double_value = transform::LSPAnyConverter::FromLSPAny(int_any); + assertEqual(42.0, double_value, "integer应该能转换为double"); + + // uinteger -> int + protocol::LSPAny uint_any(100u); + int int_value = transform::LSPAnyConverter::FromLSPAny(uint_any); + assertEqual(100, int_value, "uinteger应该能转换为int"); + + result.message = "成功"; + return result; + } + + // ==================== 错误处理测试 ==================== + + TestResult TransformerTests::testFromLSPAnyTypeMismatch() + { + TestResult result; + result.passed = true; + + protocol::LSPAny any(42); + + try + { + // 尝试将integer提取为string应该抛出异常 + (void)transform::LSPAnyConverter::FromLSPAny(any); + result.passed = false; + result.message = "应该抛出ConversionError异常"; + } + catch (const transform::ConversionError& e) + { + // 预期的异常 + result.message = "成功"; + } + catch (...) + { + result.passed = false; + result.message = "抛出了错误的异常类型"; + } + + return result; + } + + TestResult TransformerTests::testFromLSPAnyInvalidNumber() + { + TestResult result; + result.passed = true; + + protocol::LSPAny any("not a number"); + + try + { + // 尝试将string提取为int应该抛出异常 + (void)transform::LSPAnyConverter::FromLSPAny(any); + result.passed = false; + result.message = "应该抛出ConversionError异常"; + } + catch (const transform::ConversionError& e) + { + // 预期的异常 + result.message = "成功"; + } + catch (...) + { + result.passed = false; + result.message = "抛出了错误的异常类型"; + } + + return result; + } + + TestResult TransformerTests::testFromLSPAnyInvalidArray() + { + TestResult result; + result.passed = true; + + protocol::LSPAny any(42); + + try + { + // 尝试将integer提取为vector应该抛出异常 + (void)transform::LSPAnyConverter::FromLSPAny>(any); + result.passed = false; + result.message = "应该抛出ConversionError异常"; + } + catch (const transform::ConversionError& e) + { + // 预期的异常 + result.message = "成功"; + } + catch (...) + { + result.passed = false; + result.message = "抛出了错误的异常类型"; + } + + return result; + } + + // ==================== 嵌套结构测试 ==================== + + TestResult TransformerTests::testNestedVector() + { + TestResult result; + result.passed = true; + + std::vector> nested = { { 1, 2 }, { 3, 4, 5 } }; + auto any = transform::LSPAnyConverter::ToLSPAny(nested); + + assertTrue(any.Is(), "应该转换为LSPArray"); + const auto& arr = any.Get(); + assertEqual(size_t(2), arr.size(), "外层数组应该有2个元素"); + assertTrue(arr[0].Is(), "第一个元素应该是LSPArray"); + + auto result_vec = transform::LSPAnyConverter::FromLSPAny>>(any); + assertEqual(size_t(2), result_vec.size(), "应该有2个子vector"); + assertEqual(size_t(2), result_vec[0].size(), "第一个子vector应该有2个元素"); + assertEqual(1, result_vec[0][0], "第一个元素应该为1"); + + result.message = "成功"; + return result; + } + + TestResult TransformerTests::testNestedLSPObject() + { + TestResult result; + result.passed = true; + + // 直接使用 LSPObject 创建嵌套结构:{"outer": {"inner": 42}} + protocol::LSPObject innerObj; + innerObj["inner"] = protocol::LSPAny(42); + + protocol::LSPObject outerObj; + outerObj["outer"] = protocol::LSPAny(innerObj); + + auto any = transform::LSPAnyConverter::ToLSPAny(outerObj); + assertTrue(any.Is(), "应该保持为LSPObject"); + + // 提取为 LSPObject,然后手动检查嵌套值 + auto result_obj = transform::LSPAnyConverter::FromLSPAny(any); + assertTrue(result_obj.count("outer") > 0, "应该包含'outer'键"); + assertTrue(result_obj.at("outer").Is(), "'outer'应该是LSPObject"); + + const auto& inner_obj = result_obj.at("outer").Get(); + assertTrue(inner_obj.count("inner") > 0, "应该包含'inner'键"); + assertEqual(42, inner_obj.at("inner").Get(), "嵌套值应该为42"); + + result.message = "成功"; + return result; + } + + TestResult TransformerTests::testMixedTypeNesting() + { + TestResult result; + result.passed = true; + + // 直接使用 LSPArray 和 LSPObject 创建混合嵌套结构 + protocol::LSPArray arr; + arr.push_back(protocol::LSPAny(1)); + arr.push_back(protocol::LSPAny(nullptr)); // 表示 nullopt + arr.push_back(protocol::LSPAny(3)); + + protocol::LSPObject obj; + obj["data"] = protocol::LSPAny(arr); + + auto any = transform::LSPAnyConverter::ToLSPAny(obj); + assertTrue(any.Is(), "应该保持为LSPObject"); + + const auto& retrieved_obj = any.Get(); + const auto& retrieved_arr = retrieved_obj.at("data").Get(); + assertEqual(size_t(3), retrieved_arr.size(), "数组应该有3个元素"); + assertTrue(retrieved_arr[0].Is(), "第一个元素应该是integer"); + assertTrue(retrieved_arr[1].Is(), "第二个元素应该是null"); + assertTrue(retrieved_arr[2].Is(), "第三个元素应该是integer"); + + result.message = "成功"; + return result; + } + +} // namespace lsp::test diff --git a/lsp-server/test/test_lsp_any/transformer_test.hpp b/lsp-server/test/test_lsp_any/transformer_test.hpp new file mode 100644 index 0000000..668f792 --- /dev/null +++ b/lsp-server/test/test_lsp_any/transformer_test.hpp @@ -0,0 +1,62 @@ +#pragma once +#include "test_framework.hpp" + +namespace lsp::test +{ + // Transformer 转换功能测试类 + class TransformerTests + { + public: + static void registerTests(TestRunner& runner); + + private: + // ==================== ToLSPAny 基本类型测试 ==================== + static TestResult testToLSPAnyBoolean(); + static TestResult testToLSPAnyInteger(); + static TestResult testToLSPAnyUInteger(); + static TestResult testToLSPAnyFloat(); + static TestResult testToLSPAnyDouble(); + static TestResult testToLSPAnyString(); + static TestResult testToLSPAnyCString(); + + // ==================== ToLSPAny 容器类型测试 ==================== + static TestResult testToLSPAnyVector(); + static TestResult testToLSPAnyMap(); + static TestResult testToLSPAnyOptionalValue(); + static TestResult testToLSPAnyOptionalNullopt(); + + // ==================== ToLSPAny LSP类型测试 ==================== + static TestResult testToLSPAnyLSPObject(); + static TestResult testToLSPAnyLSPArray(); + static TestResult testToLSPAnyLSPAny(); + + // ==================== FromLSPAny 基本类型测试 ==================== + static TestResult testFromLSPAnyBoolean(); + static TestResult testFromLSPAnyInteger(); + static TestResult testFromLSPAnyString(); + static TestResult testFromLSPAnyDouble(); + + // ==================== FromLSPAny 容器类型测试 ==================== + static TestResult testFromLSPAnyVector(); + static TestResult testFromLSPAnyOptionalValue(); + static TestResult testFromLSPAnyOptionalNull(); + static TestResult testFromLSPAnyLSPObject(); + static TestResult testFromLSPAnyLSPArray(); + + // ==================== 数字类型转换测试 ==================== + static TestResult testExtractNumberFromInteger(); + static TestResult testExtractNumberFromUInteger(); + static TestResult testExtractNumberFromDecimal(); + static TestResult testExtractNumberCrossType(); + + // ==================== 错误处理测试 ==================== + static TestResult testFromLSPAnyTypeMismatch(); + static TestResult testFromLSPAnyInvalidNumber(); + static TestResult testFromLSPAnyInvalidArray(); + + // ==================== 嵌套结构测试 ==================== + static TestResult testNestedVector(); + static TestResult testNestedLSPObject(); + static TestResult testMixedTypeNesting(); + }; +}