From 74e0a16776373d827f2ee5d2c941dc90ebc673da Mon Sep 17 00:00:00 2001 From: Jia Sui Date: Wed, 30 Sep 2026 21:32:07 +0800 Subject: [PATCH] =?UTF-8?q?fix(engine):=20=E8=AE=A9=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E7=9A=84=20main=20=E6=8E=A5=E4=BD=8F=E9=80=83=E9=80=B8?= =?UTF-8?q?=E7=9A=84=E5=BC=82=E5=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 这些测试用 throw 报告断言失败,但 13 个测试的 main 没有顶层 try,异常一路逃出去就会 走 std::terminate -> abort。abort 不做栈展开,ScopedDataDirectoryCleanup 的析构不执行, 测试建的临时目录和用户词库句柄留在原地;失败原因也不会打到 stderr,ctest 只能看到一段 崩溃,排查时得先猜是哪个断言。 按仓里已有的写法把函数体挪进 run_test(),main 变成一层薄包装,catch 里打 what() 再返回 1。 fuzzy_pinyin 的 create_directories 原本落在 try 之前,一并挪进去。 只是给失败换一条能读到的出口,没有改任何断言或阈值。 --- engine/contracts/webview/test.cpp | 17 ++++++++++++++++- engine/tests/src/test_candidate_removal.cpp | 17 ++++++++++++++++- engine/tests/src/test_data_path.cpp | 16 +++++++++++++++- engine/tests/src/test_english_input_session.cpp | 16 +++++++++++++++- engine/tests/src/test_fuzzy_pinyin.cpp | 4 +++- engine/tests/src/test_jianpin_input_session.cpp | 16 +++++++++++++++- engine/tests/src/test_local_modes.cpp | 16 +++++++++++++++- .../src/test_mixed_expressive_input_session.cpp | 16 +++++++++++++++- engine/tests/src/test_neural_sentence.cpp | 15 ++++++++++++++- engine/tests/src/test_nine_key_session.cpp | 17 ++++++++++++++++- engine/tests/src/test_platform_api.cpp | 16 +++++++++++++++- .../src/test_quick_phrase_input_session.cpp | 16 +++++++++++++++- engine/tests/src/test_runtime_isolation.cpp | 16 +++++++++++++++- engine/tests/src/test_segmentation_contract.cpp | 16 +++++++++++++++- .../tests/src/test_temporary_input_session.cpp | 16 +++++++++++++++- 15 files changed, 215 insertions(+), 15 deletions(-) diff --git a/engine/contracts/webview/test.cpp b/engine/contracts/webview/test.cpp index af2f8412..844788a8 100644 --- a/engine/contracts/webview/test.cpp +++ b/engine/contracts/webview/test.cpp @@ -2,8 +2,9 @@ #include #include #include +#include -int main(int argc, char **argv) +int run_test(int argc, char **argv) { if (argc != 2) return 2; @@ -25,4 +26,18 @@ int main(int argc, char **argv) } } std::cout << fixtures.as_array().size() << " shared fixtures passed\n"; + return 0; +} + +int main(int argc, char **argv) +{ + try + { + return run_test(argc, argv); + } + catch (const std::exception &error) + { + std::fprintf(stderr, "%s\n", error.what()); + return 1; + } } diff --git a/engine/tests/src/test_candidate_removal.cpp b/engine/tests/src/test_candidate_removal.cpp index 41b48cfa..628b7395 100644 --- a/engine/tests/src/test_candidate_removal.cpp +++ b/engine/tests/src/test_candidate_removal.cpp @@ -9,6 +9,7 @@ #include #include #include +#include namespace { @@ -65,7 +66,7 @@ std::size_t index_of(const Session &session, const std::string &word) } } // namespace -int main() +int run_test() { using namespace metasequoia; const auto root = std::filesystem::temp_directory_path() / @@ -224,4 +225,18 @@ int main() require(main_before == bytes(resources / assets::main_dictionary) && english_before == bytes(resources / assets::english_dictionary), "Removal modified immutable resources"); + return 0; +} + +int main() +{ + try + { + return run_test(); + } + catch (const std::exception &error) + { + std::fprintf(stderr, "%s\n", error.what()); + return 1; + } } diff --git a/engine/tests/src/test_data_path.cpp b/engine/tests/src/test_data_path.cpp index 153b9007..0b150195 100644 --- a/engine/tests/src/test_data_path.cpp +++ b/engine/tests/src/test_data_path.cpp @@ -3,6 +3,7 @@ #include #include #include +#include namespace { @@ -19,7 +20,7 @@ void set_data_directory(const std::filesystem::path &path) } } // namespace -int main() +int run_test() { const std::filesystem::path expected = std::filesystem::temp_directory_path() / std::filesystem::u8path("metasequoia-ime-词库"); @@ -39,3 +40,16 @@ int main() } return 0; } + +int main() +{ + try + { + return run_test(); + } + catch (const std::exception &error) + { + std::fprintf(stderr, "%s\n", error.what()); + return 1; + } +} diff --git a/engine/tests/src/test_english_input_session.cpp b/engine/tests/src/test_english_input_session.cpp index 22641221..ea5c576e 100644 --- a/engine/tests/src/test_english_input_session.cpp +++ b/engine/tests/src/test_english_input_session.cpp @@ -13,6 +13,7 @@ #include #include #include +#include namespace { @@ -112,7 +113,7 @@ void prepare_main_database(const std::filesystem::path &directory, bool two_cand } } // namespace -int main() +int run_test() { const auto suffix = std::to_string(std::chrono::high_resolution_clock::now().time_since_epoch().count()); const std::filesystem::path root = @@ -271,3 +272,16 @@ int main() return 0; } + +int main() +{ + try + { + return run_test(); + } + catch (const std::exception &error) + { + std::fprintf(stderr, "%s\n", error.what()); + return 1; + } +} diff --git a/engine/tests/src/test_fuzzy_pinyin.cpp b/engine/tests/src/test_fuzzy_pinyin.cpp index 53b113f3..e38eeff8 100644 --- a/engine/tests/src/test_fuzzy_pinyin.cpp +++ b/engine/tests/src/test_fuzzy_pinyin.cpp @@ -43,7 +43,6 @@ int main() const auto directory = std::filesystem::temp_directory_path() / ("msime-fuzzy-" + std::to_string(std::chrono::steady_clock::now().time_since_epoch().count())); - std::filesystem::create_directories(directory); struct Cleanup { std::filesystem::path path; @@ -55,6 +54,9 @@ int main() } cleanup{directory}; try { + // 建目录也得在 try 里:这是这批测试最靠前的一次可能失败的系统调用,漏在外面就又变成 + // 逃出 main 的异常,ctest 只看得到 0xC0000409,看不到到底哪个路径被拒了。 + std::filesystem::create_directories(directory); sqlite3 *db = nullptr; require(sqlite3_open((directory / "msime.db").u8string().c_str(), &db) == SQLITE_OK, "open fixture"); const auto insert_weighted = [&](const std::string &key, const std::string &word, std::int64_t weight) { diff --git a/engine/tests/src/test_jianpin_input_session.cpp b/engine/tests/src/test_jianpin_input_session.cpp index 07c17b8a..0a0eaf6b 100644 --- a/engine/tests/src/test_jianpin_input_session.cpp +++ b/engine/tests/src/test_jianpin_input_session.cpp @@ -13,6 +13,7 @@ #include #include #include +#include namespace { @@ -114,7 +115,7 @@ void write_file(const std::filesystem::path &path, const std::string &contents) } } // namespace -int main() +int run_test() { const auto suffix = std::to_string(std::chrono::high_resolution_clock::now().time_since_epoch().count()); const std::filesystem::path root = std::filesystem::temp_directory_path() / ("metasequoia-jianpin-" + suffix); @@ -229,3 +230,16 @@ int main() return 0; } + +int main() +{ + try + { + return run_test(); + } + catch (const std::exception &error) + { + std::fprintf(stderr, "%s\n", error.what()); + return 1; + } +} diff --git a/engine/tests/src/test_local_modes.cpp b/engine/tests/src/test_local_modes.cpp index f97344f5..7748aa51 100644 --- a/engine/tests/src/test_local_modes.cpp +++ b/engine/tests/src/test_local_modes.cpp @@ -12,6 +12,7 @@ #include #include #include +#include namespace { @@ -75,7 +76,7 @@ void require_words(const std::vector &actual, const std::array expected_dates = { @@ -284,3 +285,16 @@ int main() std::filesystem::remove_all(quick_phrase_directory); return 0; } + +int main() +{ + try + { + return run_test(); + } + catch (const std::exception &error) + { + std::fprintf(stderr, "%s\n", error.what()); + return 1; + } +} diff --git a/engine/tests/src/test_mixed_expressive_input_session.cpp b/engine/tests/src/test_mixed_expressive_input_session.cpp index ae66d051..67593427 100644 --- a/engine/tests/src/test_mixed_expressive_input_session.cpp +++ b/engine/tests/src/test_mixed_expressive_input_session.cpp @@ -12,6 +12,7 @@ #include #include #include +#include namespace { @@ -138,7 +139,7 @@ bool has_source(const metasequoia::InputSession &session, CandidateSource source } } // namespace -int main() +int run_test() { const auto suffix = std::to_string(std::chrono::high_resolution_clock::now().time_since_epoch().count()); const std::filesystem::path root = @@ -279,3 +280,16 @@ int main() return 0; } + +int main() +{ + try + { + return run_test(); + } + catch (const std::exception &error) + { + std::fprintf(stderr, "%s\n", error.what()); + return 1; + } +} diff --git a/engine/tests/src/test_neural_sentence.cpp b/engine/tests/src/test_neural_sentence.cpp index 59ef7aee..832ddb57 100644 --- a/engine/tests/src/test_neural_sentence.cpp +++ b/engine/tests/src/test_neural_sentence.cpp @@ -116,7 +116,7 @@ double reference_score(const neural::SentenceModel &model, const std::string &co } } // namespace -int main() +int run_test() { const metasequoia::RuntimePaths paths = metasequoia::RuntimePaths::legacy(); const neural::SentenceModel *model = neural::shared_sentence_model( @@ -356,3 +356,16 @@ int main() std::printf("Neural sentence reranking test passed.\n"); return 0; } + +int main() +{ + try + { + return run_test(); + } + catch (const std::exception &error) + { + std::fprintf(stderr, "%s\n", error.what()); + return 1; + } +} diff --git a/engine/tests/src/test_nine_key_session.cpp b/engine/tests/src/test_nine_key_session.cpp index 1d13b632..bdf51e8e 100644 --- a/engine/tests/src/test_nine_key_session.cpp +++ b/engine/tests/src/test_nine_key_session.cpp @@ -6,6 +6,7 @@ #include #include #include "contracts/assets/assets.h" +#include using namespace metasequoia; void require(bool ok, const char *message) @@ -26,7 +27,7 @@ void type(Session &session, const std::string &digits) for (char digit : digits) require(session.character(digit).handled, "digit unhandled"); } -int main() +int run_test() { const auto directory = std::filesystem::temp_directory_path() / @@ -181,4 +182,18 @@ int main() require(failed_learning.commit == "米" && failed_learning.diagnostic && failing.snapshot().preedit.empty(), "failed learning lost commit or diagnostic"); std::cout << "Nine-key input contract passed\n"; + return 0; +} + +int main() +{ + try + { + return run_test(); + } + catch (const std::exception &error) + { + std::fprintf(stderr, "%s\n", error.what()); + return 1; + } } diff --git a/engine/tests/src/test_platform_api.cpp b/engine/tests/src/test_platform_api.cpp index c1088374..235fd715 100644 --- a/engine/tests/src/test_platform_api.cpp +++ b/engine/tests/src/test_platform_api.cpp @@ -2,6 +2,7 @@ #include #include +#include #ifdef _WIN32 static_assert(std::is_same_v); @@ -16,8 +17,21 @@ static_assert(std::is_same_v); void test_public_session_interface(); -int main() +int run_test() { test_public_session_interface(); return 0; } + +int main() +{ + try + { + return run_test(); + } + catch (const std::exception &error) + { + std::fprintf(stderr, "%s\n", error.what()); + return 1; + } +} diff --git a/engine/tests/src/test_quick_phrase_input_session.cpp b/engine/tests/src/test_quick_phrase_input_session.cpp index 604dadcb..758474e5 100644 --- a/engine/tests/src/test_quick_phrase_input_session.cpp +++ b/engine/tests/src/test_quick_phrase_input_session.cpp @@ -16,6 +16,7 @@ #include #include #include +#include namespace { @@ -268,7 +269,7 @@ void test_slot_cap() } } // namespace -int main() +int run_test() { const auto suffix = std::to_string(std::chrono::high_resolution_clock::now().time_since_epoch().count()); const std::filesystem::path root = @@ -283,3 +284,16 @@ int main() test_slot_cap(); return 0; } + +int main() +{ + try + { + return run_test(); + } + catch (const std::exception &error) + { + std::fprintf(stderr, "%s\n", error.what()); + return 1; + } +} diff --git a/engine/tests/src/test_runtime_isolation.cpp b/engine/tests/src/test_runtime_isolation.cpp index 959c10c3..aa5a5e2b 100644 --- a/engine/tests/src/test_runtime_isolation.cpp +++ b/engine/tests/src/test_runtime_isolation.cpp @@ -13,6 +13,7 @@ #include #include #include +#include namespace { @@ -794,8 +795,21 @@ void test_runtime_isolation() } } -int main() +int run_test() { test_runtime_isolation(); return 0; } + +int main() +{ + try + { + return run_test(); + } + catch (const std::exception &error) + { + std::fprintf(stderr, "%s\n", error.what()); + return 1; + } +} diff --git a/engine/tests/src/test_segmentation_contract.cpp b/engine/tests/src/test_segmentation_contract.cpp index 02839106..5d8c7c41 100644 --- a/engine/tests/src/test_segmentation_contract.cpp +++ b/engine/tests/src/test_segmentation_contract.cpp @@ -4,6 +4,7 @@ #include #include #include +#include namespace { std::filesystem::path CreatePinyinCacheDatabase() @@ -45,7 +46,7 @@ std::filesystem::path CreatePinyinCacheDatabase() return path; } } // namespace -int main() +int run_test() { const auto path = CreatePinyinCacheDatabase(); int result = 0; @@ -68,3 +69,16 @@ int main() std::filesystem::remove(path); return result; } + +int main() +{ + try + { + return run_test(); + } + catch (const std::exception &error) + { + std::fprintf(stderr, "%s\n", error.what()); + return 1; + } +} diff --git a/engine/tests/src/test_temporary_input_session.cpp b/engine/tests/src/test_temporary_input_session.cpp index 1c198e96..33b554ec 100644 --- a/engine/tests/src/test_temporary_input_session.cpp +++ b/engine/tests/src/test_temporary_input_session.cpp @@ -10,6 +10,7 @@ #include #include #include +#include namespace { @@ -67,7 +68,7 @@ void type(metasequoia::InputSession &session, const std::string &text) } } // namespace -int main() +int run_test() { const auto suffix = std::to_string(std::chrono::high_resolution_clock::now().time_since_epoch().count()); const auto data_directory = std::filesystem::temp_directory_path() / ("metasequoia-temporary-mode-" + suffix); @@ -242,3 +243,16 @@ int main() return 0; } + +int main() +{ + try + { + return run_test(); + } + catch (const std::exception &error) + { + std::fprintf(stderr, "%s\n", error.what()); + return 1; + } +}