Add tests
This commit is contained in:
1 parent
6d606580e0
commit
fca15d898d
12 files changed
+427
-80
No files matched your search
+4
-3
@@ -1,9 +1,10 @@
|
||||
build/
|
||||
Testing/
|
||||
log.*
|
||||
# /.clangd
|
||||
# /CMakeLists.txt
|
||||
# /CMakePresets.json
|
||||
/tests/CMakeLists.txt
|
||||
/tests/CMakePresets.json
|
||||
# /tests/CMakeLists.txt
|
||||
# /tests/CMakePresets.json
|
||||
/.vs
|
||||
build-release/
|
||||
build-release/
|
||||
@@ -5,6 +5,8 @@ set(PROJECT_NAME "MetasequoiaImeServer")
|
||||
cmake_policy(SET CMP0167 NEW)
|
||||
project(${PROJECT_NAME} VERSION 1.0)
|
||||
|
||||
include(CTest)
|
||||
|
||||
# Boost
|
||||
set(Boost_ROOT "C:/Users/sonnycalcr/scoop/apps/boost/current")
|
||||
set (Boost_USE_STATIC_LIBS ON)
|
||||
@@ -146,3 +148,7 @@ add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
|
||||
$<$<CONFIG:Release>:${WEBVIEW2_VCPKG_ROOT}/bin/WebView2Loader.dll>
|
||||
$<TARGET_FILE_DIR:${PROJECT_NAME}>
|
||||
)
|
||||
|
||||
if(BUILD_TESTING)
|
||||
add_subdirectory(tests)
|
||||
endif()
|
||||
@@ -1,4 +1,5 @@
|
||||
build/
|
||||
Testing/
|
||||
.vs/
|
||||
out/
|
||||
*.db
|
||||
*.db
|
||||
@@ -0,0 +1,61 @@
|
||||
set(TEST_EXECUTABLE_NAME "MetasequoiaImeServerTests")
|
||||
|
||||
set(TEST_SOURCE_FILES
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/src/main.cpp"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/src/test_shuangpin_scheme.cpp"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/src/test_quanpin_scheme.cpp"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/src/test_shuangpin_query.cpp"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/src/test_engine_shuangpin_session.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/src/config/ime_config.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/src/session/engine_input_session.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/src/utils/common_utils.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/MetasequoiaImeEngine/core/ime_session.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/MetasequoiaImeEngine/common/string_utils.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/MetasequoiaImeEngine/providers/provider_registry.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/MetasequoiaImeEngine/providers/pinyin_candidate_provider.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/MetasequoiaImeEngine/quanpin/engine.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/MetasequoiaImeEngine/quanpin/quanpin_dictionary.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/MetasequoiaImeEngine/schemes/shuangpin_scheme.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/MetasequoiaImeEngine/schemes/quanpin_scheme.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/MetasequoiaImeEngine/quanpin/quanpin_query.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/MetasequoiaImeEngine/shuangpin/engine.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/MetasequoiaImeEngine/shuangpin/shuangpin_dictionary.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/MetasequoiaImeEngine/shuangpin/shuangpin_query.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/MetasequoiaImeEngine/shuangpin/shuangpin_utils.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/MetasequoiaImeEngine/googlepinyinime-rev/src/share/dictbuilder.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/MetasequoiaImeEngine/googlepinyinime-rev/src/share/dictlist.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/MetasequoiaImeEngine/googlepinyinime-rev/src/share/dicttrie.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/MetasequoiaImeEngine/googlepinyinime-rev/src/share/lpicache.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/MetasequoiaImeEngine/googlepinyinime-rev/src/share/matrixsearch.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/MetasequoiaImeEngine/googlepinyinime-rev/src/share/mystdlib.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/MetasequoiaImeEngine/googlepinyinime-rev/src/share/ngram.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/MetasequoiaImeEngine/googlepinyinime-rev/src/share/pinyinime.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/MetasequoiaImeEngine/googlepinyinime-rev/src/share/searchutility.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/MetasequoiaImeEngine/googlepinyinime-rev/src/share/spellingtable.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/MetasequoiaImeEngine/googlepinyinime-rev/src/share/spellingtrie.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/MetasequoiaImeEngine/googlepinyinime-rev/src/share/splparser.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/MetasequoiaImeEngine/googlepinyinime-rev/src/share/sync.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/MetasequoiaImeEngine/googlepinyinime-rev/src/share/userdict.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/MetasequoiaImeEngine/googlepinyinime-rev/src/share/utf16char.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/MetasequoiaImeEngine/googlepinyinime-rev/src/share/utf16reader.cpp"
|
||||
)
|
||||
|
||||
add_executable(${TEST_EXECUTABLE_NAME} ${TEST_SOURCE_FILES})
|
||||
|
||||
target_include_directories(${TEST_EXECUTABLE_NAME}
|
||||
PRIVATE
|
||||
"${CMAKE_SOURCE_DIR}"
|
||||
"${CMAKE_SOURCE_DIR}/src"
|
||||
"${CMAKE_SOURCE_DIR}/MetasequoiaImeEngine/utfcpp/source"
|
||||
)
|
||||
|
||||
target_link_libraries(${TEST_EXECUTABLE_NAME}
|
||||
PRIVATE
|
||||
fmt::fmt
|
||||
spdlog::spdlog
|
||||
Boost::locale
|
||||
Boost::json
|
||||
unofficial::sqlite3::sqlite3
|
||||
)
|
||||
|
||||
add_test(NAME ${TEST_EXECUTABLE_NAME} COMMAND ${TEST_EXECUTABLE_NAME})
|
||||
+90
-2
@@ -1,3 +1,91 @@
|
||||
# About
|
||||
# Tests
|
||||
|
||||
Test.
|
||||
This project now includes a lightweight regression test target wired into the main CMake build.
|
||||
|
||||
## What It Covers
|
||||
|
||||
Current tests focus on high-value IME behaviors that are easy to accidentally break during refactors:
|
||||
|
||||
- `shuangpin` scheme request construction
|
||||
- `quanpin` scheme request construction
|
||||
- case-preserving shuangpin segmentation
|
||||
- full-helpcode detection
|
||||
- `engine + shuangpin` continuation behavior
|
||||
- cloud suggestion trigger timing for shuangpin
|
||||
|
||||
The goal is not to fully simulate the whole IME UI pipeline yet. The goal is to protect the core input rules and
|
||||
session behavior.
|
||||
|
||||
## Build The Tests
|
||||
|
||||
From the repository root:
|
||||
|
||||
```powershell
|
||||
cmake --build build --config Debug --target MetasequoiaImeServerTests
|
||||
```
|
||||
|
||||
## Run The Tests
|
||||
|
||||
Recommended:
|
||||
|
||||
```powershell
|
||||
ctest --test-dir build -C Debug --output-on-failure
|
||||
```
|
||||
|
||||
You can also run from inside the build directory:
|
||||
|
||||
```powershell
|
||||
cd build
|
||||
ctest -C Debug --output-on-failure
|
||||
```
|
||||
|
||||
## Common Pitfall
|
||||
|
||||
If you run `ctest` from the source root directly, you may see:
|
||||
|
||||
```text
|
||||
No tests were found!!!
|
||||
```
|
||||
|
||||
That usually means `ctest` is looking at the source directory instead of the build directory.
|
||||
|
||||
Use one of these instead:
|
||||
|
||||
```powershell
|
||||
ctest --test-dir build -C Debug --output-on-failure
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```powershell
|
||||
cd build
|
||||
ctest -C Debug --output-on-failure
|
||||
```
|
||||
|
||||
## Test Layout
|
||||
|
||||
- `tests/src/main.cpp`
|
||||
- tiny custom test runner
|
||||
- `tests/includes/test_framework.h`
|
||||
- lightweight assertion helpers
|
||||
- `tests/src/test_shuangpin_scheme.cpp`
|
||||
- shuangpin scheme unit tests
|
||||
- `tests/src/test_quanpin_scheme.cpp`
|
||||
- quanpin scheme unit tests
|
||||
- `tests/src/test_shuangpin_query.cpp`
|
||||
- query/helper function tests
|
||||
- `tests/src/test_engine_shuangpin_session.cpp`
|
||||
- session-level behavior tests
|
||||
|
||||
## Recommended Practice
|
||||
|
||||
When you change input behavior, add a regression test if the change affects:
|
||||
|
||||
- input segmentation
|
||||
- space/enter/backspace behavior
|
||||
- helpcode handling
|
||||
- continuation after candidate selection
|
||||
- create-word flow
|
||||
- cloud suggestion timing
|
||||
|
||||
The best tests in this project are the ones that capture a concrete input example that previously broke.
|
||||
@@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace test
|
||||
{
|
||||
struct TestCase
|
||||
{
|
||||
std::string name;
|
||||
std::function<void()> fn;
|
||||
};
|
||||
|
||||
std::vector<TestCase> ®istry();
|
||||
|
||||
struct Registrar
|
||||
{
|
||||
Registrar(const char *name, std::function<void()> fn);
|
||||
};
|
||||
}
|
||||
|
||||
#define TEST_CASE(name) \
|
||||
static void name(); \
|
||||
static test::Registrar name##_registrar(#name, name); \
|
||||
static void name()
|
||||
|
||||
#define REQUIRE(expr) \
|
||||
do \
|
||||
{ \
|
||||
if (!(expr)) \
|
||||
{ \
|
||||
throw std::runtime_error("Requirement failed: " #expr); \
|
||||
} \
|
||||
} while (false)
|
||||
|
||||
#define REQUIRE_EQ(lhs, rhs) \
|
||||
do \
|
||||
{ \
|
||||
const auto &_lhs = (lhs); \
|
||||
const auto &_rhs = (rhs); \
|
||||
if (!(_lhs == _rhs)) \
|
||||
{ \
|
||||
throw std::runtime_error("Requirement failed: " #lhs " == " #rhs); \
|
||||
} \
|
||||
} while (false)
|
||||
+34
-52
@@ -1,67 +1,49 @@
|
||||
#include <exception>
|
||||
#include <fmt/core.h>
|
||||
#include <iostream>
|
||||
#include <sqlite3.h>
|
||||
#include "tests/includes/test_framework.h"
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
namespace test
|
||||
{
|
||||
std::cout << "This is a code template project for Windows cpp!" << '\n';
|
||||
fmt::print("Hello World from fmt!!\n");
|
||||
std::vector<TestCase> ®istry()
|
||||
{
|
||||
static std::vector<TestCase> tests;
|
||||
return tests;
|
||||
}
|
||||
|
||||
sqlite3 *db = nullptr;
|
||||
char *errMsg = nullptr;
|
||||
Registrar::Registrar(const char *name, std::function<void()> fn)
|
||||
{
|
||||
registry().push_back({name, std::move(fn)});
|
||||
}
|
||||
} // namespace test
|
||||
|
||||
// Create or open database
|
||||
if (sqlite3_open("test.db", &db) != SQLITE_OK)
|
||||
int main()
|
||||
{
|
||||
int failures = 0;
|
||||
for (const auto &test_case : test::registry())
|
||||
{
|
||||
std::cerr << "Cannot open database: " << sqlite3_errmsg(db) << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Create table
|
||||
const char *createTableSQL = "CREATE TABLE IF NOT EXISTS users ("
|
||||
"id INTEGER PRIMARY KEY AUTOINCREMENT,"
|
||||
"name TEXT NOT NULL,"
|
||||
"age INTEGER);";
|
||||
|
||||
if (sqlite3_exec(db, createTableSQL, nullptr, nullptr, &errMsg) != SQLITE_OK)
|
||||
{
|
||||
std::cerr << "Failed to create table: " << errMsg << std::endl;
|
||||
sqlite3_free(errMsg);
|
||||
sqlite3_close(db);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Insert data
|
||||
const char *insertSQL = "INSERT INTO users (name, age) VALUES ('Alice', 25);";
|
||||
if (sqlite3_exec(db, insertSQL, nullptr, nullptr, &errMsg) != SQLITE_OK)
|
||||
{
|
||||
std::cerr << "Failed to insert data: " << errMsg << std::endl;
|
||||
sqlite3_free(errMsg);
|
||||
sqlite3_close(db);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Query data
|
||||
const char *querySQL = "SELECT id, name, age FROM users;";
|
||||
auto callback = [](void *, int argc, char **argv, char **colName) -> int {
|
||||
for (int i = 0; i < argc; ++i)
|
||||
try
|
||||
{
|
||||
std::cout << colName[i] << ": " << (argv[i] ? argv[i] : "NULL") << " | ";
|
||||
test_case.fn();
|
||||
fmt::print("[PASS] {}\n", test_case.name);
|
||||
}
|
||||
std::cout << "\n";
|
||||
return 0;
|
||||
};
|
||||
catch (const std::exception &ex)
|
||||
{
|
||||
++failures;
|
||||
fmt::print(stderr, "[FAIL] {}: {}\n", test_case.name, ex.what());
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
++failures;
|
||||
fmt::print(stderr, "[FAIL] {}: unknown exception\n", test_case.name);
|
||||
}
|
||||
}
|
||||
|
||||
if (sqlite3_exec(db, querySQL, callback, nullptr, &errMsg) != SQLITE_OK)
|
||||
if (failures > 0)
|
||||
{
|
||||
std::cerr << "Failed to query data: " << errMsg << std::endl;
|
||||
sqlite3_free(errMsg);
|
||||
sqlite3_close(db);
|
||||
fmt::print(stderr, "{} test(s) failed\n", failures);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Close database
|
||||
sqlite3_close(db);
|
||||
std::cout << "All done.\n";
|
||||
fmt::print("{} test(s) passed\n", test::registry().size());
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
#include "tests/includes/test_framework.h"
|
||||
#include "src/session/engine_input_session.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
void InputLetters(EngineInputSession &session, const std::string &keys)
|
||||
{
|
||||
for (const char ch : keys)
|
||||
{
|
||||
const bool is_upper = ch >= 'A' && ch <= 'Z';
|
||||
const char upper = is_upper ? ch : static_cast<char>(ch - ('a' - 'A'));
|
||||
session.handle_key(static_cast<UINT>(upper), 0, static_cast<WCHAR>(ch));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE(EngineShuangpinSessionContinuesCompositionWithoutHelpcode)
|
||||
{
|
||||
EngineInputSession session(SchemeType::Shuangpin);
|
||||
InputLetters(session, "xitele");
|
||||
|
||||
const auto transition = session.advance_composition_after_selection("xi", "西");
|
||||
REQUIRE(transition.continues_composition);
|
||||
REQUIRE_EQ(session.get_pinyin_sequence(), std::string("tele"));
|
||||
}
|
||||
|
||||
TEST_CASE(EngineShuangpinSessionContinuesCompositionWithSingleHelpcode)
|
||||
{
|
||||
EngineInputSession session(SchemeType::Shuangpin);
|
||||
InputLetters(session, "xitelea");
|
||||
|
||||
const auto transition = session.advance_composition_after_selection("xi", "西");
|
||||
REQUIRE(transition.continues_composition);
|
||||
REQUIRE_EQ(session.get_pinyin_sequence(), std::string("tele"));
|
||||
}
|
||||
|
||||
TEST_CASE(EngineShuangpinSessionContinuesCompositionWithDoubleHelpcode)
|
||||
{
|
||||
EngineInputSession session(SchemeType::Shuangpin);
|
||||
InputLetters(session, "xiteleaA");
|
||||
|
||||
const auto transition = session.advance_composition_after_selection("xi", "西");
|
||||
REQUIRE(transition.continues_composition);
|
||||
REQUIRE_EQ(session.get_pinyin_sequence(), std::string("tele"));
|
||||
}
|
||||
|
||||
TEST_CASE(EngineShuangpinSessionCloudQueryMatchesLegacyTiming)
|
||||
{
|
||||
EngineInputSession session(SchemeType::Shuangpin);
|
||||
|
||||
InputLetters(session, "xi");
|
||||
auto state = session.get_cloud_query_state();
|
||||
REQUIRE(state.should_query);
|
||||
|
||||
session.reset_state();
|
||||
InputLetters(session, "xia");
|
||||
state = session.get_cloud_query_state();
|
||||
REQUIRE(!state.should_query);
|
||||
|
||||
session.reset_state();
|
||||
InputLetters(session, "xiA");
|
||||
state = session.get_cloud_query_state();
|
||||
REQUIRE(!state.should_query);
|
||||
|
||||
session.reset_state();
|
||||
InputLetters(session, "xI");
|
||||
state = session.get_cloud_query_state();
|
||||
REQUIRE(!state.should_query);
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
#include "tests/includes/test_framework.h"
|
||||
#include "MetasequoiaImeEngine/schemes/quanpin_scheme.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
void InputKey(QuanpinScheme &scheme, UINT vk, WCHAR wch, UINT modifiers_down = 0)
|
||||
{
|
||||
scheme.handle_key(vk, modifiers_down, wch);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE(QuanpinSchemeSpaceDoesNotResetComposition)
|
||||
{
|
||||
QuanpinScheme scheme;
|
||||
InputKey(scheme, 'N', L'n');
|
||||
InputKey(scheme, 'I', L'i');
|
||||
InputKey(scheme, VK_SPACE, L' ');
|
||||
|
||||
const QueryRequest request = scheme.build_request();
|
||||
REQUIRE(request.valid);
|
||||
REQUIRE_EQ(request.raw_input, std::string("ni"));
|
||||
REQUIRE_EQ(request.normalized_input, std::string("ni"));
|
||||
}
|
||||
|
||||
TEST_CASE(QuanpinSchemeApostropheIsPreservedInRawInput)
|
||||
{
|
||||
QuanpinScheme scheme;
|
||||
InputKey(scheme, 'X', L'x');
|
||||
InputKey(scheme, VK_OEM_7, L'\'');
|
||||
InputKey(scheme, 'I', L'i');
|
||||
|
||||
const QueryRequest request = scheme.build_request();
|
||||
REQUIRE(request.valid);
|
||||
REQUIRE_EQ(request.raw_input, std::string("x'i"));
|
||||
REQUIRE_EQ(request.normalized_input, std::string("xi"));
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
#include <fmt/core.h>
|
||||
#include <sqlite3.h>
|
||||
#include "fmt/base.h"
|
||||
#include "MetasequoiaImeEngine/shuangpin/shuangpin_dictionary.h"
|
||||
#include <chrono>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
DictionaryUlPb dict;
|
||||
std::vector<DictionaryUlPb::WordItem> result = dict.generate("ceui", "ce'ui");
|
||||
std::chrono::high_resolution_clock::time_point start = std::chrono::high_resolution_clock::now();
|
||||
result = dict.generate("name", "na'me");
|
||||
std::chrono::high_resolution_clock::time_point end = std::chrono::high_resolution_clock::now();
|
||||
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
|
||||
fmt::println("Time: {} us", duration.count());
|
||||
fmt::println("Word count: {}", result.size());
|
||||
for (const auto &[pinyin, word, weight] : result)
|
||||
{
|
||||
fmt::println("Word: {}", word);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
#include "tests/includes/test_framework.h"
|
||||
#include "MetasequoiaImeEngine/shuangpin/shuangpin_query.h"
|
||||
#include "MetasequoiaImeEngine/shuangpin/shuangpin_utils.h"
|
||||
|
||||
TEST_CASE(ApplySegmentationCasesPreservesUppercaseMarkers)
|
||||
{
|
||||
const std::string segmented = "xi'te'le'aa";
|
||||
const std::string cased = "xiteleaA";
|
||||
REQUIRE_EQ(shuangpin::apply_segmentation_cases(segmented, cased), std::string("xi'te'le'aA"));
|
||||
}
|
||||
|
||||
TEST_CASE(IsFullHelpModeRecognizesDoubleHelpcodePattern)
|
||||
{
|
||||
REQUIRE(ShuangpinUtil::IsFullHelpMode("xiteleaA"));
|
||||
REQUIRE(!ShuangpinUtil::IsFullHelpMode("xitelea"));
|
||||
REQUIRE(!ShuangpinUtil::IsFullHelpMode("xi"));
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
#include "tests/includes/test_framework.h"
|
||||
#include "MetasequoiaImeEngine/schemes/shuangpin_scheme.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
void InputKey(ShuangpinScheme &scheme, UINT vk, WCHAR wch, UINT modifiers_down = 0)
|
||||
{
|
||||
scheme.handle_key(vk, modifiers_down, wch);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE(ShuangpinSchemeBuildRequestPreservesCaseAndNormalizesQuery)
|
||||
{
|
||||
ShuangpinScheme scheme;
|
||||
InputKey(scheme, 'X', L'X', 1);
|
||||
InputKey(scheme, 'I', L'i');
|
||||
|
||||
const QueryRequest request = scheme.build_request();
|
||||
REQUIRE(request.valid);
|
||||
REQUIRE_EQ(request.raw_input, std::string("xi"));
|
||||
REQUIRE_EQ(request.raw_input_with_cases, std::string("Xi"));
|
||||
REQUIRE_EQ(request.raw_segmentation, std::string("Xi"));
|
||||
REQUIRE_EQ(request.normalized_input, std::string("xi"));
|
||||
}
|
||||
|
||||
TEST_CASE(ShuangpinSchemeSpaceDoesNotResetComposition)
|
||||
{
|
||||
ShuangpinScheme scheme;
|
||||
InputKey(scheme, 'X', L'x');
|
||||
InputKey(scheme, 'I', L'i');
|
||||
InputKey(scheme, VK_SPACE, L' ');
|
||||
|
||||
const QueryRequest request = scheme.build_request();
|
||||
REQUIRE(request.valid);
|
||||
REQUIRE_EQ(request.raw_input, std::string("xi"));
|
||||
}
|
||||
|
||||
TEST_CASE(ShuangpinSchemeEnterResetsComposition)
|
||||
{
|
||||
ShuangpinScheme scheme;
|
||||
InputKey(scheme, 'X', L'x');
|
||||
InputKey(scheme, 'I', L'i');
|
||||
InputKey(scheme, VK_RETURN, 0);
|
||||
|
||||
const QueryRequest request = scheme.build_request();
|
||||
REQUIRE(!request.valid);
|
||||
REQUIRE_EQ(request.raw_input, std::string(""));
|
||||
}
|
||||
|
||||
TEST_CASE(ShuangpinSchemeBackspaceRemovesLastInput)
|
||||
{
|
||||
ShuangpinScheme scheme;
|
||||
InputKey(scheme, 'X', L'x');
|
||||
InputKey(scheme, 'I', L'i');
|
||||
InputKey(scheme, VK_BACK, 0);
|
||||
|
||||
const QueryRequest request = scheme.build_request();
|
||||
REQUIRE(request.valid);
|
||||
REQUIRE_EQ(request.raw_input, std::string("x"));
|
||||
REQUIRE_EQ(request.raw_input_with_cases, std::string("x"));
|
||||
}
|
||||
Reference in new issue
Block a user