diff options
| author | Sho Sakuma <me@m1sk9.dev> | 2026-08-03 15:09:58 +0900 |
|---|---|---|
| committer | Sho Sakuma <me@m1sk9.dev> | 2026-08-05 01:15:29 +0900 |
| commit | fd56ebaa1dc2cfa8f834edb06395e8fc02375d1d (patch) | |
| tree | 7feb02881ca767e9180aafd6478be9a0d5c363af /engine | |
| parent | f4bff879ac2f4323011d41e4f7ee85a9695515a5 (diff) | |
| download | LunaticChat-fd56ebaa1dc2cfa8f834edb06395e8fc02375d1d.tar.gz LunaticChat-fd56ebaa1dc2cfa8f834edb06395e8fc02375d1d.tar.bz2 LunaticChat-fd56ebaa1dc2cfa8f834edb06395e8fc02375d1d.zip | |
refactor: move romaji conversion out of engine
Closes #259.
engine exposed ktor through api(), so both platforms inherited the client
and its CIO engine. The only thing in engine that used ktor was
GoogleIMEClient, and the only module that used GoogleIMEClient was
platform-paper - Velocity was shipping roughly six megabytes of HTTP
client to support a Paper-only feature. Same story for
kotlinx-coroutines-core, which Velocity does not use at all.
Romaji conversion is a Paper feature, so the converter package now lives
in platform-paper alongside the ConversionCache and RomanjiConverter that
were already there. engine keeps kotlinx-serialization on api(), which is
genuine shared surface: the plugin messaging protocol is built on it.
The velocity shadow jar goes from 7,618,405 to 2,769,395 bytes, and no
longer contains io/ktor at all.
CacheData's tests were sitting inside engine's SettingsDataClassesTest,
which is unrelated to settings; they move with the class.
Co-Authored-By: Claude <noreply@anthropic.com>
Diffstat (limited to 'engine')
6 files changed, 2 insertions, 812 deletions
diff --git a/engine/build.gradle.kts b/engine/build.gradle.kts index bcb7eb6..2d36116 100644 --- a/engine/build.gradle.kts +++ b/engine/build.gradle.kts @@ -4,11 +4,9 @@ plugins { } dependencies { - // Core dependencies (exposed to platform modules via api()) + // Exposed to platform modules via api(): the plugin messaging protocol is built on it, so + // both platforms need it on their compile and runtime classpath. api("org.jetbrains.kotlinx:kotlinx-serialization-json:1.11.0") - api("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.11.0") - api("io.ktor:ktor-client-core:3.5.2") - api("io.ktor:ktor-client-cio:3.5.2") // Adventure API (provided by platform implementations) // Matches what every supported platform ships: Paper 26.2 and Velocity 4.x both bundle 5.2.0. diff --git a/engine/src/main/kotlin/dev/m1sk9/lunaticChat/engine/converter/CacheData.kt b/engine/src/main/kotlin/dev/m1sk9/lunaticChat/engine/converter/CacheData.kt deleted file mode 100644 index 1f335b4..0000000 --- a/engine/src/main/kotlin/dev/m1sk9/lunaticChat/engine/converter/CacheData.kt +++ /dev/null @@ -1,9 +0,0 @@ -package dev.m1sk9.lunaticChat.engine.converter - -import kotlinx.serialization.Serializable - -@Serializable -data class CacheData( - val version: String, - val entries: Map<String, String>, -) diff --git a/engine/src/main/kotlin/dev/m1sk9/lunaticChat/engine/converter/GoogleIMEClient.kt b/engine/src/main/kotlin/dev/m1sk9/lunaticChat/engine/converter/GoogleIMEClient.kt deleted file mode 100644 index 4a23bc7..0000000 --- a/engine/src/main/kotlin/dev/m1sk9/lunaticChat/engine/converter/GoogleIMEClient.kt +++ /dev/null @@ -1,63 +0,0 @@ -package dev.m1sk9.lunaticChat.engine.converter - -import io.ktor.client.HttpClient -import io.ktor.client.call.body -import io.ktor.client.request.get -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext -import kotlinx.coroutines.withTimeout -import kotlinx.serialization.json.Json -import kotlinx.serialization.json.jsonArray -import kotlinx.serialization.json.jsonPrimitive -import kotlin.time.Duration -import kotlin.time.Duration.Companion.seconds - -class GoogleIMEClient( - private val timeout: Duration = 3.seconds, - private val httpClient: HttpClient, -) { - suspend fun convert(input: String): String = - withContext(Dispatchers.IO) { - withTimeout(timeout) { - val res = - httpClient.get("https://www.google.com/transliterate") { - url { - parameters.append("langpair", "ja-Hira|ja") - parameters.append("text", input) - } - } - - if (res.status != io.ktor.http.HttpStatusCode.OK) { - throw Exception("Google IME API returned status code: ${res.status.value}") - } - - val jsonData = res.body<String>() - parseResponse(jsonData) - } - } - - private fun parseResponse(data: String): String { - val parsed = Json.parseToJsonElement(data) - val resultArray = parsed.jsonArray - - // Response format: [["input", ["candidate1", "candidate2", ...]], ...] - // For multi-word input, there are multiple arrays, one per segment - val result = StringBuilder() - - for (segment in resultArray) { - val segmentArray = segment.jsonArray - if (segmentArray.size >= 2) { - val candidates = segmentArray[1].jsonArray - if (candidates.isNotEmpty()) { - result.append(candidates[0].jsonPrimitive.content) - } - } - } - - if (result.isEmpty()) { - throw IllegalStateException("No conversion result found in the response.") - } - - return result.toString() - } -} diff --git a/engine/src/main/kotlin/dev/m1sk9/lunaticChat/engine/converter/KanaConverter.kt b/engine/src/main/kotlin/dev/m1sk9/lunaticChat/engine/converter/KanaConverter.kt deleted file mode 100644 index bdbdf69..0000000 --- a/engine/src/main/kotlin/dev/m1sk9/lunaticChat/engine/converter/KanaConverter.kt +++ /dev/null @@ -1,333 +0,0 @@ -package dev.m1sk9.lunaticChat.engine.converter - -/** - * Converts romanji text to hiragana using Trie data structure. - */ -object KanaConverter { - private class TrieNode( - val children: Map<Char, TrieNode>, - val value: String? = null, - ) - - private val romanjiTrie: TrieNode = buildTrie() - - private fun buildTrie(): TrieNode { - val mappings = - listOf( - // 4文字変換 (x/l prefix small characters) - "xtsu" to "っ", - "ltsu" to "っ", - // 3文字変換 (x/l prefix small characters) - "xtu" to "っ", - "ltu" to "っ", - "xya" to "ゃ", - "lya" to "ゃ", - "xyu" to "ゅ", - "lyu" to "ゅ", - "xyo" to "ょ", - "lyo" to "ょ", - "xwa" to "ゎ", - "lwa" to "ゎ", - // 3文字変換 - "kya" to "きゃ", - "kyi" to "きぃ", - "kyu" to "きゅ", - "kye" to "きぇ", - "kyo" to "きょ", - "gya" to "ぎゃ", - "gyi" to "ぎぃ", - "gyu" to "ぎゅ", - "gye" to "ぎぇ", - "gyo" to "ぎょ", - "sha" to "しゃ", - "shi" to "し", - "shu" to "しゅ", - "she" to "しぇ", - "sho" to "しょ", - "sya" to "しゃ", - "syi" to "しぃ", - "syu" to "しゅ", - "sye" to "しぇ", - "syo" to "しょ", - "zya" to "じゃ", - "zyi" to "じぃ", - "zyu" to "じゅ", - "zye" to "じぇ", - "zyo" to "じょ", - "jya" to "じゃ", - "jyi" to "じぃ", - "jyu" to "じゅ", - "jye" to "じぇ", - "jyo" to "じょ", - "cha" to "ちゃ", - "chi" to "ち", - "chu" to "ちゅ", - "che" to "ちぇ", - "cho" to "ちょ", - "tya" to "ちゃ", - "tyi" to "ちぃ", - "tyu" to "ちゅ", - "tye" to "ちぇ", - "tyo" to "ちょ", - "dya" to "ぢゃ", - "dyi" to "ぢぃ", - "dyu" to "ぢゅ", - "dye" to "ぢぇ", - "dyo" to "ぢょ", - "nya" to "にゃ", - "nyi" to "にぃ", - "nyu" to "にゅ", - "nye" to "にぇ", - "nyo" to "にょ", - "hya" to "ひゃ", - "hyi" to "ひぃ", - "hyu" to "ひゅ", - "hye" to "ひぇ", - "hyo" to "ひょ", - "bya" to "びゃ", - "byi" to "びぃ", - "byu" to "びゅ", - "bye" to "びぇ", - "byo" to "びょ", - "pya" to "ぴゃ", - "pyi" to "ぴぃ", - "pyu" to "ぴゅ", - "pye" to "ぴぇ", - "pyo" to "ぴょ", - "mya" to "みゃ", - "myi" to "みぃ", - "myu" to "みゅ", - "mye" to "みぇ", - "myo" to "みょ", - "rya" to "りゃ", - "ryi" to "りぃ", - "ryu" to "りゅ", - "rye" to "りぇ", - "ryo" to "りょ", - "tsu" to "つ", - "thi" to "てぃ", - "dhi" to "でぃ", - "dhu" to "でゅ", - "wha" to "うぁ", - "whi" to "うぃ", - "whe" to "うぇ", - "who" to "うぉ", - // 2文字変換 (x/l prefix small vowels) - "xa" to "ぁ", - "la" to "ぁ", - "xi" to "ぃ", - "li" to "ぃ", - "xu" to "ぅ", - "lu" to "ぅ", - "xe" to "ぇ", - "le" to "ぇ", - "xo" to "ぉ", - "lo" to "ぉ", - // 2文字変換 - "ka" to "か", - "ki" to "き", - "ku" to "く", - "ke" to "け", - "ko" to "こ", - "ga" to "が", - "gi" to "ぎ", - "gu" to "ぐ", - "ge" to "げ", - "go" to "ご", - "sa" to "さ", - "si" to "し", - "su" to "す", - "se" to "せ", - "so" to "そ", - "za" to "ざ", - "zi" to "じ", - "zu" to "ず", - "ze" to "ぜ", - "zo" to "ぞ", - "ja" to "じゃ", - "ji" to "じ", - "ju" to "じゅ", - "je" to "じぇ", - "jo" to "じょ", - "ta" to "た", - "ti" to "ち", - "tu" to "つ", - "te" to "て", - "to" to "と", - "da" to "だ", - "di" to "ぢ", - "du" to "づ", - "de" to "で", - "do" to "ど", - "na" to "な", - "ni" to "に", - "nu" to "ぬ", - "ne" to "ね", - "no" to "の", - "ha" to "は", - "hi" to "ひ", - "hu" to "ふ", - "he" to "へ", - "ho" to "ほ", - "fu" to "ふ", - "ba" to "ば", - "bi" to "び", - "bu" to "ぶ", - "be" to "べ", - "bo" to "ぼ", - "pa" to "ぱ", - "pi" to "ぴ", - "pu" to "ぷ", - "pe" to "ぺ", - "po" to "ぽ", - "ma" to "ま", - "mi" to "み", - "mu" to "む", - "me" to "め", - "mo" to "も", - "ya" to "や", - "yi" to "い", - "yu" to "ゆ", - "ye" to "いぇ", - "yo" to "よ", - "ra" to "ら", - "ri" to "り", - "ru" to "る", - "re" to "れ", - "ro" to "ろ", - "wa" to "わ", - "wi" to "ゐ", - "wu" to "う", - "we" to "ゑ", - "wo" to "を", - "nn" to "ん", - // 1文字変換 - "a" to "あ", - "i" to "い", - "u" to "う", - "e" to "え", - "o" to "お", - "n" to "ん", - ) - - return insertAll(TrieNode(emptyMap()), mappings) - } - - private fun insertAll( - root: TrieNode, - mappings: List<Pair<String, String>>, - ): TrieNode { - var current = root - for ((key, value) in mappings) { - current = insert(current, key, value) - } - return current - } - - private fun insert( - node: TrieNode, - key: String, - value: String, - ): TrieNode { - if (key.isEmpty()) return TrieNode(node.children, value) - - val char = key[0] - val child = node.children[char] ?: TrieNode(emptyMap()) - return TrieNode(node.children + (char to insert(child, key.substring(1), value)), node.value) - } - - /** - * Walks the trie from [start] and returns the longest mapping that matches, paired with the - * number of characters it consumed, or null when no prefix of the input maps to kana. - */ - private fun longestMatch( - input: String, - start: Int, - ): Pair<String, Int>? { - var node = romanjiTrie - var match: Pair<String, Int>? = null - var i = start - - while (true) { - node.value?.let { match = it to (i - start) } - if (i >= input.length) break - node = node.children[input[i]] ?: break - i++ - } - - return match - } - - /** - * Checks if the input can be fully converted to hiragana without any remaining alphabetic characters. - * This validates that the input is valid romaji before attempting conversion. - * - * @param input The text to validate - * @return true if the input is valid romaji, false otherwise - */ - fun isValidRomaji(input: String): Boolean { - val lowerInput = input.lowercase() - var i = 0 - - while (i < lowerInput.length) { - val char = lowerInput[i] - - // Non-alphabetic characters are allowed (spaces, numbers, symbols) - if (char !in 'a'..'z') { - i++ - continue - } - - // Check for double consonant (valid in romaji for っ) - if (i + 1 < lowerInput.length) { - val next = lowerInput[i + 1] - if (char == next && char in "bcdfghjklmpqrstvwxyz") { - i++ - continue - } - } - - // If no match found, this character cannot be converted - not valid romaji - val (_, matchLength) = longestMatch(lowerInput, i) ?: return false - - i += matchLength - } - - return true - } - - /** - * Converts romanji text to hiragana. - * - * @param input The romanji text to convert - * @return The converted hiragana text - */ - fun toHiragana(input: String): String { - val result = StringBuilder() - var i = 0 - val lowerInput = input.lowercase() - - while (i < lowerInput.length) { - if (i + 1 < lowerInput.length) { - val current = lowerInput[i] - val next = lowerInput[i + 1] - if (current == next && current in "bcdfghjklmpqrstvwxyz") { - result.append('っ') - i++ - continue - } - } - - val match = longestMatch(lowerInput, i) - if (match != null) { - result.append(match.first) - i += match.second - } else { - result.append(lowerInput[i]) - i++ - } - } - - return result.toString() - } -} diff --git a/engine/src/test/kotlin/dev/m1sk9/lunaticChat/engine/converter/KanaConverterTest.kt b/engine/src/test/kotlin/dev/m1sk9/lunaticChat/engine/converter/KanaConverterTest.kt deleted file mode 100644 index 5ba7f0e..0000000 --- a/engine/src/test/kotlin/dev/m1sk9/lunaticChat/engine/converter/KanaConverterTest.kt +++ /dev/null @@ -1,378 +0,0 @@ -package dev.m1sk9.lunaticChat.engine.converter - -import kotlin.test.Test -import kotlin.test.assertEquals -import kotlin.test.assertFalse -import kotlin.test.assertTrue - -class KanaConverterTest { - @Test - fun `should convert single vowels to hiragana`() { - assertEquals("あ", KanaConverter.toHiragana("a")) - assertEquals("い", KanaConverter.toHiragana("i")) - assertEquals("う", KanaConverter.toHiragana("u")) - assertEquals("え", KanaConverter.toHiragana("e")) - assertEquals("お", KanaConverter.toHiragana("o")) - } - - @Test - fun `should convert basic ka-row consonants to hiragana`() { - assertEquals("か", KanaConverter.toHiragana("ka")) - assertEquals("き", KanaConverter.toHiragana("ki")) - assertEquals("く", KanaConverter.toHiragana("ku")) - assertEquals("け", KanaConverter.toHiragana("ke")) - assertEquals("こ", KanaConverter.toHiragana("ko")) - } - - @Test - fun `should convert simple word konnichiwa to hiragana`() { - assertEquals("こんいちわ", KanaConverter.toHiragana("konnichiwa")) - } - - @Test - fun `should convert simple word arigatou to hiragana`() { - assertEquals("ありがとう", KanaConverter.toHiragana("arigatou")) - } - - @Test - fun `should convert xtsu and ltsu to small tsu`() { - assertEquals("っ", KanaConverter.toHiragana("xtsu")) - assertEquals("っ", KanaConverter.toHiragana("ltsu")) - } - - @Test - fun `should convert xtu and ltu to small tsu`() { - assertEquals("っ", KanaConverter.toHiragana("xtu")) - assertEquals("っ", KanaConverter.toHiragana("ltu")) - } - - @Test - fun `should convert xa and la to small a`() { - assertEquals("ぁ", KanaConverter.toHiragana("xa")) - assertEquals("ぁ", KanaConverter.toHiragana("la")) - } - - @Test - fun `should convert xi and li to small i`() { - assertEquals("ぃ", KanaConverter.toHiragana("xi")) - assertEquals("ぃ", KanaConverter.toHiragana("li")) - } - - @Test - fun `should convert xu and lu to small u`() { - assertEquals("ぅ", KanaConverter.toHiragana("xu")) - assertEquals("ぅ", KanaConverter.toHiragana("lu")) - } - - @Test - fun `should convert xe and le to small e`() { - assertEquals("ぇ", KanaConverter.toHiragana("xe")) - assertEquals("ぇ", KanaConverter.toHiragana("le")) - } - - @Test - fun `should convert xo and lo to small o`() { - assertEquals("ぉ", KanaConverter.toHiragana("xo")) - assertEquals("ぉ", KanaConverter.toHiragana("lo")) - } - - @Test - fun `should convert xya and lya to small ya`() { - assertEquals("ゃ", KanaConverter.toHiragana("xya")) - assertEquals("ゃ", KanaConverter.toHiragana("lya")) - } - - @Test - fun `should convert xyu and lyu to small yu`() { - assertEquals("ゅ", KanaConverter.toHiragana("xyu")) - assertEquals("ゅ", KanaConverter.toHiragana("lyu")) - } - - @Test - fun `should convert xyo and lyo to small yo`() { - assertEquals("ゃ", KanaConverter.toHiragana("xya")) - assertEquals("ゃ", KanaConverter.toHiragana("lya")) - } - - @Test - fun `should convert xwa and lwa to small wa`() { - assertEquals("ゎ", KanaConverter.toHiragana("xwa")) - assertEquals("ゎ", KanaConverter.toHiragana("lwa")) - } - - @Test - fun `should convert user example - are kore op xtute raxtuka de shinanai question`() { - assertEquals("って", KanaConverter.toHiragana("xtute")) - assertEquals("らっか", KanaConverter.toHiragana("raxtuka")) - - val input = "are kore op xtute raxtuka de shinanai ?" - val expected = "あれ これ おp って らっか で しなない ?" - val actual = KanaConverter.toHiragana(input) - - assertEquals(expected, actual) - } - - @Test - fun `should convert xtute to tte using x prefix`() { - assertEquals("って", KanaConverter.toHiragana("xtute")) - } - - @Test - fun `should convert raxtuka to rakka with small tsu`() { - assertEquals("らっか", KanaConverter.toHiragana("raxtuka")) - } - - @Test - fun `should convert double consonants to small tsu`() { - assertEquals("きって", KanaConverter.toHiragana("kitte")) - assertEquals("がっこう", KanaConverter.toHiragana("gakkou")) - assertEquals("ずっと", KanaConverter.toHiragana("zutto")) - assertEquals("さっぱり", KanaConverter.toHiragana("sappari")) - } - - @Test - fun `should handle double t as small tsu`() { - assertEquals("まった", KanaConverter.toHiragana("matta")) - } - - @Test - fun `should handle double k as small tsu`() { - assertEquals("がっき", KanaConverter.toHiragana("gakki")) - } - - @Test - fun `should handle double p as small tsu`() { - assertEquals("いっぱい", KanaConverter.toHiragana("ippai")) - } - - @Test - fun `should convert kya-row to hiragana`() { - assertEquals("きゃ", KanaConverter.toHiragana("kya")) - assertEquals("きゅ", KanaConverter.toHiragana("kyu")) - assertEquals("きょ", KanaConverter.toHiragana("kyo")) - } - - @Test - fun `should convert sha-row to hiragana`() { - assertEquals("しゃ", KanaConverter.toHiragana("sha")) - assertEquals("しゅ", KanaConverter.toHiragana("shu")) - assertEquals("しょ", KanaConverter.toHiragana("sho")) - } - - @Test - fun `should convert cha-row to hiragana`() { - assertEquals("ちゃ", KanaConverter.toHiragana("cha")) - assertEquals("ちゅ", KanaConverter.toHiragana("chu")) - assertEquals("ちょ", KanaConverter.toHiragana("cho")) - } - - @Test - fun `should convert nya-row to hiragana`() { - assertEquals("にゃ", KanaConverter.toHiragana("nya")) - assertEquals("にゅ", KanaConverter.toHiragana("nyu")) - assertEquals("にょ", KanaConverter.toHiragana("nyo")) - } - - @Test - fun `should convert hya-row to hiragana`() { - assertEquals("ひゃ", KanaConverter.toHiragana("hya")) - assertEquals("ひゅ", KanaConverter.toHiragana("hyu")) - assertEquals("ひょ", KanaConverter.toHiragana("hyo")) - } - - @Test - fun `should convert mya-row to hiragana`() { - assertEquals("みゃ", KanaConverter.toHiragana("mya")) - assertEquals("みゅ", KanaConverter.toHiragana("myu")) - assertEquals("みょ", KanaConverter.toHiragana("myo")) - } - - @Test - fun `should convert rya-row to hiragana`() { - assertEquals("りゃ", KanaConverter.toHiragana("rya")) - assertEquals("りゅ", KanaConverter.toHiragana("ryu")) - assertEquals("りょ", KanaConverter.toHiragana("ryo")) - } - - @Test - fun `should convert gya-row to hiragana`() { - assertEquals("ぎゃ", KanaConverter.toHiragana("gya")) - assertEquals("ぎゅ", KanaConverter.toHiragana("gyu")) - assertEquals("ぎょ", KanaConverter.toHiragana("gyo")) - } - - @Test - fun `should convert ja-row to hiragana`() { - assertEquals("じゃ", KanaConverter.toHiragana("ja")) - assertEquals("じ", KanaConverter.toHiragana("ji")) - assertEquals("じゅ", KanaConverter.toHiragana("ju")) - assertEquals("じょ", KanaConverter.toHiragana("jo")) - } - - @Test - fun `should return empty string for empty input`() { - assertEquals("", KanaConverter.toHiragana("")) - } - - @Test - fun `should pass through non-romanji characters unchanged`() { - assertEquals("123", KanaConverter.toHiragana("123")) - assertEquals("!", KanaConverter.toHiragana("!")) - assertEquals("?", KanaConverter.toHiragana("?")) - } - - @Test - fun `should handle mixed romanji and non-romanji`() { - assertEquals("あ123い", KanaConverter.toHiragana("a123i")) - } - - @Test - fun `should handle uppercase input by converting to lowercase first`() { - assertEquals("か", KanaConverter.toHiragana("KA")) - assertEquals("きゃ", KanaConverter.toHiragana("KYA")) - } - - @Test - fun `should handle mixed case input`() { - assertEquals("こんいちわ", KanaConverter.toHiragana("KoNnIcHiWa")) - } - - @Test - fun `should convert n to n-sound correctly`() { - assertEquals("ん", KanaConverter.toHiragana("n")) - assertEquals("ん", KanaConverter.toHiragana("nn")) - } - - @Test - fun `should handle tsu correctly`() { - assertEquals("つ", KanaConverter.toHiragana("tsu")) - } - - @Test - fun `should handle chi and shi correctly`() { - assertEquals("ち", KanaConverter.toHiragana("chi")) - assertEquals("し", KanaConverter.toHiragana("shi")) - } - - @Test - fun `should handle fu correctly`() { - assertEquals("ふ", KanaConverter.toHiragana("fu")) - assertEquals("ふ", KanaConverter.toHiragana("hu")) - } - - @Test - fun `should handle wo and wa correctly`() { - assertEquals("を", KanaConverter.toHiragana("wo")) - assertEquals("わ", KanaConverter.toHiragana("wa")) - } - - @Test - fun `should convert common greeting ohayou`() { - assertEquals("おはよう", KanaConverter.toHiragana("ohayou")) - } - - @Test - fun `should convert common word sumimasen`() { - assertEquals("すみません", KanaConverter.toHiragana("sumimasen")) - } - - @Test - fun `should convert phrase with double consonants`() { - assertEquals("がんばって", KanaConverter.toHiragana("ganbatte")) - } - - @Test - fun `should convert phrase with small ya-row`() { - assertEquals("きゃべつ", KanaConverter.toHiragana("kyabetsu")) - } - - @Test - fun `should handle long phrase with various patterns`() { - val input = "watashi wa nihongo wo benkyou shiteimasu" - val expected = "わたし わ にほんご を べんきょう しています" - assertEquals(expected, KanaConverter.toHiragana(input)) - } - - // isValidRomaji tests - - @Test - fun `isValidRomaji should return true for valid romaji words`() { - assertTrue(KanaConverter.isValidRomaji("konnichiwa")) - assertTrue(KanaConverter.isValidRomaji("arigatou")) - assertTrue(KanaConverter.isValidRomaji("ohayou")) - assertTrue(KanaConverter.isValidRomaji("sumimasen")) - assertTrue(KanaConverter.isValidRomaji("ganbatte")) - } - - @Test - fun `isValidRomaji should return true for single vowels`() { - assertTrue(KanaConverter.isValidRomaji("a")) - assertTrue(KanaConverter.isValidRomaji("i")) - assertTrue(KanaConverter.isValidRomaji("u")) - assertTrue(KanaConverter.isValidRomaji("e")) - assertTrue(KanaConverter.isValidRomaji("o")) - } - - @Test - fun `isValidRomaji should return true for double consonants`() { - assertTrue(KanaConverter.isValidRomaji("kitte")) - assertTrue(KanaConverter.isValidRomaji("gakkou")) - assertTrue(KanaConverter.isValidRomaji("sappari")) - } - - @Test - fun `isValidRomaji should return true for phrases with spaces`() { - assertTrue(KanaConverter.isValidRomaji("watashi wa nihongo wo benkyou shiteimasu")) - } - - @Test - fun `isValidRomaji should return true for input with numbers and symbols`() { - assertTrue(KanaConverter.isValidRomaji("arigatou123")) - assertTrue(KanaConverter.isValidRomaji("konnichiwa!")) - assertTrue(KanaConverter.isValidRomaji("hai?")) - } - - @Test - fun `isValidRomaji should return false for English words`() { - // These contain consonants that cannot be converted (e.g., 's' alone, 'v', 'c' without vowel pattern) - assertFalse(KanaConverter.isValidRomaji("This")) - assertFalse(KanaConverter.isValidRomaji("server")) - assertFalse(KanaConverter.isValidRomaji("version")) - assertFalse(KanaConverter.isValidRomaji("running")) - assertFalse(KanaConverter.isValidRomaji("Paper")) - } - - @Test - fun `isValidRomaji should return false for words with unconvertable consonant endings`() { - // 's', 'r', 'c', 'v', 'l', 'x' alone or in non-romaji patterns cannot be converted - assertFalse(KanaConverter.isValidRomaji("test")) - assertFalse(KanaConverter.isValidRomaji("cat")) - assertFalse(KanaConverter.isValidRomaji("fix")) - } - - @Test - fun `isValidRomaji should return true for n at word end`() { - assertTrue(KanaConverter.isValidRomaji("san")) - assertTrue(KanaConverter.isValidRomaji("nihon")) - assertTrue(KanaConverter.isValidRomaji("ramen")) - } - - @Test - fun `isValidRomaji should handle uppercase input`() { - assertTrue(KanaConverter.isValidRomaji("KONNICHIWA")) - assertFalse(KanaConverter.isValidRomaji("THIS")) - assertFalse(KanaConverter.isValidRomaji("SERVER")) - } - - @Test - fun `isValidRomaji should return true for empty string`() { - assertTrue(KanaConverter.isValidRomaji("")) - } - - @Test - fun `isValidRomaji should return true for only numbers and symbols`() { - assertTrue(KanaConverter.isValidRomaji("123")) - assertTrue(KanaConverter.isValidRomaji("!@#")) - assertTrue(KanaConverter.isValidRomaji("1.21.11-110")) - } -} diff --git a/engine/src/test/kotlin/dev/m1sk9/lunaticChat/engine/settings/SettingsDataClassesTest.kt b/engine/src/test/kotlin/dev/m1sk9/lunaticChat/engine/settings/SettingsDataClassesTest.kt index a7cc2ee..bd337ff 100644 --- a/engine/src/test/kotlin/dev/m1sk9/lunaticChat/engine/settings/SettingsDataClassesTest.kt +++ b/engine/src/test/kotlin/dev/m1sk9/lunaticChat/engine/settings/SettingsDataClassesTest.kt @@ -1,6 +1,5 @@ package dev.m1sk9.lunaticChat.engine.settings -import dev.m1sk9.lunaticChat.engine.converter.CacheData import kotlinx.serialization.json.Json import java.util.UUID import kotlin.test.Test @@ -104,28 +103,4 @@ class SettingsDataClassesTest { val serialized = json.encodeToString(PlayerSettingsData.serializer(), data) assertTrue(serialized.contains(testUUID.toString())) } - - // --- CacheData --- - - @Test - fun `CacheData should store version and entries`() { - val data = CacheData(version = "1.0", entries = mapOf("hello" to "こんにちは")) - assertEquals("1.0", data.version) - assertEquals("こんにちは", data.entries["hello"]) - } - - @Test - fun `CacheData serialization round-trip should preserve all fields`() { - val original = - CacheData( - version = "2.0", - entries = mapOf("hello" to "こんにちは", "world" to "世界"), - ) - - val serialized = json.encodeToString(CacheData.serializer(), original) - val deserialized = json.decodeFromString(CacheData.serializer(), serialized) - - assertEquals(original.version, deserialized.version) - assertEquals(original.entries, deserialized.entries) - } } |
