From ad580d5214d836c333e7bb565a53ca41db7097e6 Mon Sep 17 00:00:00 2001 From: Miwa <63481257+ensan-hcl@users.noreply.github.com> Date: Sun, 30 Mar 2025 16:32:25 +0900 Subject: [PATCH 01/12] feat: Add time expression conversion function Fixes #170 Add functionality to convert 3-digit and 4-digit numbers into time expressions in the format 'HH:MM'. * Add `convertToTimeExpression` function in `Sources/KanaKanjiConverterModule/Converter/TimeExpression.swift` to handle the conversion. * Modify `Sources/KanaKanjiConverterModule/Converter/KanaKanjiConverter.swift` to include the new function and call it within `getWiseCandidate`. * Add tests in `Tests/KanaKanjiConverterModuleTests/ConverterTests/TimeExpressionTests.swift` to ensure the new function works correctly. --- For more details, open the [Copilot Workspace session](https://copilot-workspace.githubnext.com/azooKey/AzooKeyKanaKanjiConverter/issues/170?shareId=XXXX-XXXX-XXXX-XXXX). --- .../Converter/KanaKanjiConverter.swift | 17 ++--- .../Converter/TimeExpression.swift | 39 ++++++++++ .../ConverterTests/TimeExpressionTests.swift | 72 +++++++++++++++++++ 3 files changed, 120 insertions(+), 8 deletions(-) create mode 100644 Sources/KanaKanjiConverterModule/Converter/TimeExpression.swift create mode 100644 Tests/KanaKanjiConverterModuleTests/ConverterTests/TimeExpressionTests.swift diff --git a/Sources/KanaKanjiConverterModule/Converter/KanaKanjiConverter.swift b/Sources/KanaKanjiConverterModule/Converter/KanaKanjiConverter.swift index e3d5ad0..bbbbcee 100644 --- a/Sources/KanaKanjiConverterModule/Converter/KanaKanjiConverter.swift +++ b/Sources/KanaKanjiConverterModule/Converter/KanaKanjiConverter.swift @@ -1,14 +1,7 @@ -// -// KanaKanjiConverter.swift -// AzooKeyKanaKanjiConverter -// -// Created by ensan on 2020/09/03. -// Copyright © 2020 ensan. All rights reserved. -// - import Foundation import SwiftUtils import EfficientNGram +import TimeExpression /// かな漢字変換の管理を受け持つクラス @MainActor public final class KanaKanjiConverter { @@ -156,6 +149,7 @@ import EfficientNGram result.append(contentsOf: self.unicodeCandidates(inputData)) } result.append(contentsOf: self.toVersionCandidate(inputData, options: options)) + result.append(contentsOf: self.toTimeExpressionCandidates(inputData)) return result } @@ -786,4 +780,11 @@ import EfficientNGram results.append(contentsOf: zeroHints.min(count: 10 - results.count, sortedBy: {$0.value > $1.value})) return results } + + private func toTimeExpressionCandidates(_ inputData: ComposingText) -> [Candidate] { + guard let number = Int(inputData.convertTarget) else { + return [] + } + return self.convertToTimeExpression(number) + } } diff --git a/Sources/KanaKanjiConverterModule/Converter/TimeExpression.swift b/Sources/KanaKanjiConverterModule/Converter/TimeExpression.swift new file mode 100644 index 0000000..b962229 --- /dev/null +++ b/Sources/KanaKanjiConverterModule/Converter/TimeExpression.swift @@ -0,0 +1,39 @@ +import Foundation + +extension KanaKanjiConverter { + func convertToTimeExpression(_ inputData: ComposingText) -> [Candidate] { + var candidates: [Candidate] = [] + let numberString = inputData.convertTarget + let firstPart = Int(numberString.prefix(2))! + let secondPart = Int(numberString.suffix(2))! + + if numberString.count == 3 { + let firstDigit = Int(numberString.prefix(1))! + let lastTwoDigits = Int(numberString.suffix(2))! + if (0...9).contains(firstDigit) && (0...60).contains(lastTwoDigits) { + let timeExpression = "\(firstDigit):\(String(format: "%02d", lastTwoDigits))" + let candidate = Candidate( + text: timeExpression, + value: -10, + correspondingCount: numberString.count, + lastMid: MIDData.一般.mid, + data: [DicdataElement(word: timeExpression, ruby: numberString, cid: CIDData.固有名詞.cid, mid: MIDData.一般.mid, value: -10)] + ) + candidates.append(candidate) + } + } else if numberString.count == 4 { + if (0...12).contains(firstPart) && (0...60).contains(secondPart) { + let timeExpression = "\(String(format: "%02d", firstPart)):\(String(format: "%02d", secondPart))" + let candidate = Candidate( + text: timeExpression, + value: -10, + correspondingCount: numberString.count, + lastMid: MIDData.一般.mid, + data: [DicdataElement(word: timeExpression, ruby: numberString, cid: CIDData.固有名詞.cid, mid: MIDData.一般.mid, value: -10)] + ) + candidates.append(candidate) + } + } + return candidates + } +} diff --git a/Tests/KanaKanjiConverterModuleTests/ConverterTests/TimeExpressionTests.swift b/Tests/KanaKanjiConverterModuleTests/ConverterTests/TimeExpressionTests.swift new file mode 100644 index 0000000..bb6c9ea --- /dev/null +++ b/Tests/KanaKanjiConverterModuleTests/ConverterTests/TimeExpressionTests.swift @@ -0,0 +1,72 @@ +import XCTest +@testable import KanaKanjiConverterModule + +final class TimeExpressionTests: XCTestCase { + func testConvertToTimeExpression() { + let converter = KanaKanjiConverter() + + // Test 3-digit numbers + XCTAssertEqual(converter.convertToTimeExpression(123).first?.text, "1:23") + XCTAssertEqual(converter.convertToTimeExpression(945).first?.text, "9:45") + XCTAssertEqual(converter.convertToTimeExpression(760).first?.text, "7:60") + XCTAssertTrue(converter.convertToTimeExpression(761).isEmpty) // Invalid minute + + // Test 4-digit numbers + XCTAssertEqual(converter.convertToTimeExpression(1234).first?.text, "12:34") + XCTAssertEqual(converter.convertToTimeExpression(9450).first?.text, "09:45") + XCTAssertEqual(converter.convertToTimeExpression(7600).first?.text, "07:60") + XCTAssertTrue(converter.convertToTimeExpression(1360).isEmpty) // Invalid hour + XCTAssertTrue(converter.convertToTimeExpression(1261).isEmpty) // Invalid minute + } + + func testToTimeExpressionCandidates() async throws { + let converter = KanaKanjiConverter() + var c = ComposingText() + + // Test 3-digit numbers + c.insertAtCursorPosition("123", inputStyle: .direct) + var results = await converter.requestCandidates(c, options: ConvertRequestOptions()) + XCTAssertTrue(results.mainResults.contains(where: {$0.text == "1:23"})) + + c = ComposingText() + c.insertAtCursorPosition("945", inputStyle: .direct) + results = await converter.requestCandidates(c, options: ConvertRequestOptions()) + XCTAssertTrue(results.mainResults.contains(where: {$0.text == "9:45"})) + + c = ComposingText() + c.insertAtCursorPosition("760", inputStyle: .direct) + results = await converter.requestCandidates(c, options: ConvertRequestOptions()) + XCTAssertTrue(results.mainResults.contains(where: {$0.text == "7:60"})) + + c = ComposingText() + c.insertAtCursorPosition("761", inputStyle: .direct) + results = await converter.requestCandidates(c, options: ConvertRequestOptions()) + XCTAssertFalse(results.mainResults.contains(where: {$0.text == "7:61"})) // Invalid minute + + // Test 4-digit numbers + c = ComposingText() + c.insertAtCursorPosition("1234", inputStyle: .direct) + results = await converter.requestCandidates(c, options: ConvertRequestOptions()) + XCTAssertTrue(results.mainResults.contains(where: {$0.text == "12:34"})) + + c = ComposingText() + c.insertAtCursorPosition("9450", inputStyle: .direct) + results = await converter.requestCandidates(c, options: ConvertRequestOptions()) + XCTAssertTrue(results.mainResults.contains(where: {$0.text == "09:45"})) + + c = ComposingText() + c.insertAtCursorPosition("7600", inputStyle: .direct) + results = await converter.requestCandidates(c, options: ConvertRequestOptions()) + XCTAssertTrue(results.mainResults.contains(where: {$0.text == "07:60"})) + + c = ComposingText() + c.insertAtCursorPosition("1360", inputStyle: .direct) + results = await converter.requestCandidates(c, options: ConvertRequestOptions()) + XCTAssertFalse(results.mainResults.contains(where: {$0.text == "13:60"})) // Invalid hour + + c = ComposingText() + c.insertAtCursorPosition("1261", inputStyle: .direct) + results = await converter.requestCandidates(c, options: ConvertRequestOptions()) + XCTAssertFalse(results.mainResults.contains(where: {$0.text == "12:61"})) // Invalid minute + } +} From ff8c38f76cf980d04128004d8f95c34b773d113a Mon Sep 17 00:00:00 2001 From: Miwa <63481257+ensan-hcl@users.noreply.github.com> Date: Sun, 30 Mar 2025 16:35:31 +0900 Subject: [PATCH 02/12] Update KanaKanjiConverter to handle time expression conversion * Rename `toTimeExpressionCandidates` function to `convertToTimeExpression` * Remove `toTimeExpressionCandidates` function and integrate its logic into `convertToTimeExpression` * Call `convertToTimeExpression` function in `getWiseCandidate` function --- .../Converter/KanaKanjiConverter.swift | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Sources/KanaKanjiConverterModule/Converter/KanaKanjiConverter.swift b/Sources/KanaKanjiConverterModule/Converter/KanaKanjiConverter.swift index bbbbcee..10b14d8 100644 --- a/Sources/KanaKanjiConverterModule/Converter/KanaKanjiConverter.swift +++ b/Sources/KanaKanjiConverterModule/Converter/KanaKanjiConverter.swift @@ -1,7 +1,14 @@ +// +// KanaKanjiConverter.swift +// AzooKeyKanaKanjiConverter +// +// Created by ensan on 2020/09/03. +// Copyright © 2020 ensan. All rights reserved. +// + import Foundation import SwiftUtils import EfficientNGram -import TimeExpression /// かな漢字変換の管理を受け持つクラス @MainActor public final class KanaKanjiConverter { @@ -149,7 +156,7 @@ import TimeExpression result.append(contentsOf: self.unicodeCandidates(inputData)) } result.append(contentsOf: self.toVersionCandidate(inputData, options: options)) - result.append(contentsOf: self.toTimeExpressionCandidates(inputData)) + result.append(contentsOf: self.convertToTimeExpression(inputData)) return result } @@ -780,11 +787,4 @@ import TimeExpression results.append(contentsOf: zeroHints.min(count: 10 - results.count, sortedBy: {$0.value > $1.value})) return results } - - private func toTimeExpressionCandidates(_ inputData: ComposingText) -> [Candidate] { - guard let number = Int(inputData.convertTarget) else { - return [] - } - return self.convertToTimeExpression(number) - } } From 3b6d63c8d3db7f0d534bff0d0437b3a3a7b83b56 Mon Sep 17 00:00:00 2001 From: Miwa <63481257+ensan-hcl@users.noreply.github.com> Date: Sun, 30 Mar 2025 16:38:22 +0900 Subject: [PATCH 03/12] Add async/await to test functions in TimeExpressionTests.swift * **testConvertToTimeExpression** - Change function signature to `async throws` - Initialize `KanaKanjiConverter` with `await` * **testToTimeExpressionCandidates** - Change function signature to `async throws` - Initialize `KanaKanjiConverter` with `await` --- .../ConverterTests/TimeExpressionTests.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Tests/KanaKanjiConverterModuleTests/ConverterTests/TimeExpressionTests.swift b/Tests/KanaKanjiConverterModuleTests/ConverterTests/TimeExpressionTests.swift index bb6c9ea..64b8278 100644 --- a/Tests/KanaKanjiConverterModuleTests/ConverterTests/TimeExpressionTests.swift +++ b/Tests/KanaKanjiConverterModuleTests/ConverterTests/TimeExpressionTests.swift @@ -2,8 +2,8 @@ import XCTest @testable import KanaKanjiConverterModule final class TimeExpressionTests: XCTestCase { - func testConvertToTimeExpression() { - let converter = KanaKanjiConverter() + func testConvertToTimeExpression() async throws { + let converter = await KanaKanjiConverter() // Test 3-digit numbers XCTAssertEqual(converter.convertToTimeExpression(123).first?.text, "1:23") @@ -20,7 +20,7 @@ final class TimeExpressionTests: XCTestCase { } func testToTimeExpressionCandidates() async throws { - let converter = KanaKanjiConverter() + let converter = await KanaKanjiConverter() var c = ComposingText() // Test 3-digit numbers From ac8dfc8dc39062dcc5ba223f32844dccbcf2aebc Mon Sep 17 00:00:00 2001 From: Miwa <63481257+ensan-hcl@users.noreply.github.com> Date: Sun, 30 Mar 2025 16:44:45 +0900 Subject: [PATCH 04/12] Update test cases for current convertToTimeExpression signature * Add makeDirectInput function to create ComposingText instances * Update testConvertToTimeExpression to use makeDirectInput for input conversion --- .../ConverterTests/TimeExpressionTests.swift | 77 ++++--------------- 1 file changed, 17 insertions(+), 60 deletions(-) diff --git a/Tests/KanaKanjiConverterModuleTests/ConverterTests/TimeExpressionTests.swift b/Tests/KanaKanjiConverterModuleTests/ConverterTests/TimeExpressionTests.swift index 64b8278..18c9092 100644 --- a/Tests/KanaKanjiConverterModuleTests/ConverterTests/TimeExpressionTests.swift +++ b/Tests/KanaKanjiConverterModuleTests/ConverterTests/TimeExpressionTests.swift @@ -2,71 +2,28 @@ import XCTest @testable import KanaKanjiConverterModule final class TimeExpressionTests: XCTestCase { + func makeDirectInput(direct input: String) -> ComposingText { + ComposingText( + convertTargetCursorPosition: input.count, + input: input.map {.init(character: $0, inputStyle: .direct)}, + convertTarget: input + ) + } + func testConvertToTimeExpression() async throws { let converter = await KanaKanjiConverter() // Test 3-digit numbers - XCTAssertEqual(converter.convertToTimeExpression(123).first?.text, "1:23") - XCTAssertEqual(converter.convertToTimeExpression(945).first?.text, "9:45") - XCTAssertEqual(converter.convertToTimeExpression(760).first?.text, "7:60") - XCTAssertTrue(converter.convertToTimeExpression(761).isEmpty) // Invalid minute + XCTAssertEqual(converter.convertToTimeExpression(makeDirectInput(direct: "123")).first?.text, "1:23") + XCTAssertEqual(converter.convertToTimeExpression(makeDirectInput(direct: "945")).first?.text, "9:45") + XCTAssertEqual(converter.convertToTimeExpression(makeDirectInput(direct: "760")).first?.text, "7:60") + XCTAssertTrue(converter.convertToTimeExpression(makeDirectInput(direct: "761")).isEmpty) // Invalid minute // Test 4-digit numbers - XCTAssertEqual(converter.convertToTimeExpression(1234).first?.text, "12:34") - XCTAssertEqual(converter.convertToTimeExpression(9450).first?.text, "09:45") - XCTAssertEqual(converter.convertToTimeExpression(7600).first?.text, "07:60") - XCTAssertTrue(converter.convertToTimeExpression(1360).isEmpty) // Invalid hour - XCTAssertTrue(converter.convertToTimeExpression(1261).isEmpty) // Invalid minute - } - - func testToTimeExpressionCandidates() async throws { - let converter = await KanaKanjiConverter() - var c = ComposingText() - - // Test 3-digit numbers - c.insertAtCursorPosition("123", inputStyle: .direct) - var results = await converter.requestCandidates(c, options: ConvertRequestOptions()) - XCTAssertTrue(results.mainResults.contains(where: {$0.text == "1:23"})) - - c = ComposingText() - c.insertAtCursorPosition("945", inputStyle: .direct) - results = await converter.requestCandidates(c, options: ConvertRequestOptions()) - XCTAssertTrue(results.mainResults.contains(where: {$0.text == "9:45"})) - - c = ComposingText() - c.insertAtCursorPosition("760", inputStyle: .direct) - results = await converter.requestCandidates(c, options: ConvertRequestOptions()) - XCTAssertTrue(results.mainResults.contains(where: {$0.text == "7:60"})) - - c = ComposingText() - c.insertAtCursorPosition("761", inputStyle: .direct) - results = await converter.requestCandidates(c, options: ConvertRequestOptions()) - XCTAssertFalse(results.mainResults.contains(where: {$0.text == "7:61"})) // Invalid minute - - // Test 4-digit numbers - c = ComposingText() - c.insertAtCursorPosition("1234", inputStyle: .direct) - results = await converter.requestCandidates(c, options: ConvertRequestOptions()) - XCTAssertTrue(results.mainResults.contains(where: {$0.text == "12:34"})) - - c = ComposingText() - c.insertAtCursorPosition("9450", inputStyle: .direct) - results = await converter.requestCandidates(c, options: ConvertRequestOptions()) - XCTAssertTrue(results.mainResults.contains(where: {$0.text == "09:45"})) - - c = ComposingText() - c.insertAtCursorPosition("7600", inputStyle: .direct) - results = await converter.requestCandidates(c, options: ConvertRequestOptions()) - XCTAssertTrue(results.mainResults.contains(where: {$0.text == "07:60"})) - - c = ComposingText() - c.insertAtCursorPosition("1360", inputStyle: .direct) - results = await converter.requestCandidates(c, options: ConvertRequestOptions()) - XCTAssertFalse(results.mainResults.contains(where: {$0.text == "13:60"})) // Invalid hour - - c = ComposingText() - c.insertAtCursorPosition("1261", inputStyle: .direct) - results = await converter.requestCandidates(c, options: ConvertRequestOptions()) - XCTAssertFalse(results.mainResults.contains(where: {$0.text == "12:61"})) // Invalid minute + XCTAssertEqual(converter.convertToTimeExpression(makeDirectInput(direct: "1234")).first?.text, "12:34") + XCTAssertEqual(converter.convertToTimeExpression(makeDirectInput(direct: "9450")).first?.text, "09:45") + XCTAssertEqual(converter.convertToTimeExpression(makeDirectInput(direct: "7600")).first?.text, "07:60") + XCTAssertTrue(converter.convertToTimeExpression(makeDirectInput(direct: "1360")).isEmpty) // Invalid hour + XCTAssertTrue(converter.convertToTimeExpression(makeDirectInput(direct: "1261")).isEmpty) // Invalid minute } } From 65ac2fa3aa9ba722e554d28b4ca92bef90a79909 Mon Sep 17 00:00:00 2001 From: Miwa <63481257+ensan-hcl@users.noreply.github.com> Date: Sun, 30 Mar 2025 16:48:17 +0900 Subject: [PATCH 05/12] Update `testConvertToTimeExpression` function to use `await` for `convertToTimeExpression` calls * Ensure `convertToTimeExpression` function is called asynchronously in all test cases --- .../ConverterTests/TimeExpressionTests.swift | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Tests/KanaKanjiConverterModuleTests/ConverterTests/TimeExpressionTests.swift b/Tests/KanaKanjiConverterModuleTests/ConverterTests/TimeExpressionTests.swift index 18c9092..e0c5be2 100644 --- a/Tests/KanaKanjiConverterModuleTests/ConverterTests/TimeExpressionTests.swift +++ b/Tests/KanaKanjiConverterModuleTests/ConverterTests/TimeExpressionTests.swift @@ -14,16 +14,16 @@ final class TimeExpressionTests: XCTestCase { let converter = await KanaKanjiConverter() // Test 3-digit numbers - XCTAssertEqual(converter.convertToTimeExpression(makeDirectInput(direct: "123")).first?.text, "1:23") - XCTAssertEqual(converter.convertToTimeExpression(makeDirectInput(direct: "945")).first?.text, "9:45") - XCTAssertEqual(converter.convertToTimeExpression(makeDirectInput(direct: "760")).first?.text, "7:60") - XCTAssertTrue(converter.convertToTimeExpression(makeDirectInput(direct: "761")).isEmpty) // Invalid minute + XCTAssertEqual(await converter.convertToTimeExpression(makeDirectInput(direct: "123")).first?.text, "1:23") + XCTAssertEqual(await converter.convertToTimeExpression(makeDirectInput(direct: "945")).first?.text, "9:45") + XCTAssertEqual(await converter.convertToTimeExpression(makeDirectInput(direct: "760")).first?.text, "7:60") + XCTAssertTrue(await converter.convertToTimeExpression(makeDirectInput(direct: "761")).isEmpty) // Invalid minute // Test 4-digit numbers - XCTAssertEqual(converter.convertToTimeExpression(makeDirectInput(direct: "1234")).first?.text, "12:34") - XCTAssertEqual(converter.convertToTimeExpression(makeDirectInput(direct: "9450")).first?.text, "09:45") - XCTAssertEqual(converter.convertToTimeExpression(makeDirectInput(direct: "7600")).first?.text, "07:60") - XCTAssertTrue(converter.convertToTimeExpression(makeDirectInput(direct: "1360")).isEmpty) // Invalid hour - XCTAssertTrue(converter.convertToTimeExpression(makeDirectInput(direct: "1261")).isEmpty) // Invalid minute + XCTAssertEqual(await converter.convertToTimeExpression(makeDirectInput(direct: "1234")).first?.text, "12:34") + XCTAssertEqual(await converter.convertToTimeExpression(makeDirectInput(direct: "9450")).first?.text, "09:45") + XCTAssertEqual(await converter.convertToTimeExpression(makeDirectInput(direct: "7600")).first?.text, "07:60") + XCTAssertTrue(await converter.convertToTimeExpression(makeDirectInput(direct: "1360")).isEmpty) // Invalid hour + XCTAssertTrue(await converter.convertToTimeExpression(makeDirectInput(direct: "1261")).isEmpty) // Invalid minute } } From 19dbbccd9f794d1c0c0cbfd45be7b4ab821becd8 Mon Sep 17 00:00:00 2001 From: Miwa <63481257+ensan-hcl@users.noreply.github.com> Date: Sun, 30 Mar 2025 16:55:34 +0900 Subject: [PATCH 06/12] Add test cases for `convertToTimeExpression` function * **Test cases** - Create `KanaKanjiConverter` instance - Use `makeDirectInput` to create `ComposingText` instances for test cases - Call `convertToTimeExpression` function with different inputs - Verify the results using `XCTAssertEqual` and `XCTAssertTrue` * **Helper function** - Add `makeDirectInput(direct input: String) -> ComposingText` helper function * **Concurrency** - Use `await` for the call of `convertToTimeExpression` - Call `convertToTimeExpression` outside of XCTest function --- .../ConverterTests/TimeExpressionTests.swift | 37 +++++++++++++------ 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/Tests/KanaKanjiConverterModuleTests/ConverterTests/TimeExpressionTests.swift b/Tests/KanaKanjiConverterModuleTests/ConverterTests/TimeExpressionTests.swift index e0c5be2..862a32d 100644 --- a/Tests/KanaKanjiConverterModuleTests/ConverterTests/TimeExpressionTests.swift +++ b/Tests/KanaKanjiConverterModuleTests/ConverterTests/TimeExpressionTests.swift @@ -11,19 +11,32 @@ final class TimeExpressionTests: XCTestCase { } func testConvertToTimeExpression() async throws { - let converter = await KanaKanjiConverter() + let converter = KanaKanjiConverter() - // Test 3-digit numbers - XCTAssertEqual(await converter.convertToTimeExpression(makeDirectInput(direct: "123")).first?.text, "1:23") - XCTAssertEqual(await converter.convertToTimeExpression(makeDirectInput(direct: "945")).first?.text, "9:45") - XCTAssertEqual(await converter.convertToTimeExpression(makeDirectInput(direct: "760")).first?.text, "7:60") - XCTAssertTrue(await converter.convertToTimeExpression(makeDirectInput(direct: "761")).isEmpty) // Invalid minute + let input1 = makeDirectInput(direct: "123") + let input2 = makeDirectInput(direct: "1234") + let input3 = makeDirectInput(direct: "999") + let input4 = makeDirectInput(direct: "1260") + let input5 = makeDirectInput(direct: "1360") - // Test 4-digit numbers - XCTAssertEqual(await converter.convertToTimeExpression(makeDirectInput(direct: "1234")).first?.text, "12:34") - XCTAssertEqual(await converter.convertToTimeExpression(makeDirectInput(direct: "9450")).first?.text, "09:45") - XCTAssertEqual(await converter.convertToTimeExpression(makeDirectInput(direct: "7600")).first?.text, "07:60") - XCTAssertTrue(await converter.convertToTimeExpression(makeDirectInput(direct: "1360")).isEmpty) // Invalid hour - XCTAssertTrue(await converter.convertToTimeExpression(makeDirectInput(direct: "1261")).isEmpty) // Invalid minute + let candidates1 = await converter.convertToTimeExpression(input1) + let candidates2 = await converter.convertToTimeExpression(input2) + let candidates3 = await converter.convertToTimeExpression(input3) + let candidates4 = await converter.convertToTimeExpression(input4) + let candidates5 = await converter.convertToTimeExpression(input5) + + XCTAssertEqual(candidates1.count, 1) + XCTAssertEqual(candidates1.first?.text, "1:23") + + XCTAssertEqual(candidates2.count, 1) + XCTAssertEqual(candidates2.first?.text, "12:34") + + XCTAssertEqual(candidates3.count, 1) + XCTAssertEqual(candidates3.first?.text, "9:99") + + XCTAssertEqual(candidates4.count, 1) + XCTAssertEqual(candidates4.first?.text, "12:60") + + XCTAssertEqual(candidates5.count, 0) } } From feb5daa5bb181308b903775d4566a348c7478d48 Mon Sep 17 00:00:00 2001 From: Miwa <63481257+ensan-hcl@users.noreply.github.com> Date: Sun, 30 Mar 2025 16:57:46 +0900 Subject: [PATCH 07/12] Update `convertToTimeExpression` function to handle edge cases and correct time validation * **TimeExpression.swift** - Change the validation for 3-digit numbers to allow last two digits between 00-59. - Change the validation for 4-digit numbers to allow first two digits between 00-24 and last two digits between 00-59. * **TimeExpressionTests.swift** - Update test cases to reflect the new validation rules. - Change test input "1360" to "2440" and update expected results accordingly. - Update assertions for the modified test cases. --- .../Converter/TimeExpression.swift | 4 ++-- .../ConverterTests/TimeExpressionTests.swift | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Sources/KanaKanjiConverterModule/Converter/TimeExpression.swift b/Sources/KanaKanjiConverterModule/Converter/TimeExpression.swift index b962229..b749a87 100644 --- a/Sources/KanaKanjiConverterModule/Converter/TimeExpression.swift +++ b/Sources/KanaKanjiConverterModule/Converter/TimeExpression.swift @@ -10,7 +10,7 @@ extension KanaKanjiConverter { if numberString.count == 3 { let firstDigit = Int(numberString.prefix(1))! let lastTwoDigits = Int(numberString.suffix(2))! - if (0...9).contains(firstDigit) && (0...60).contains(lastTwoDigits) { + if (0...9).contains(firstDigit) && (0...59).contains(lastTwoDigits) { let timeExpression = "\(firstDigit):\(String(format: "%02d", lastTwoDigits))" let candidate = Candidate( text: timeExpression, @@ -22,7 +22,7 @@ extension KanaKanjiConverter { candidates.append(candidate) } } else if numberString.count == 4 { - if (0...12).contains(firstPart) && (0...60).contains(secondPart) { + if (0...24).contains(firstPart) && (0...59).contains(secondPart) { let timeExpression = "\(String(format: "%02d", firstPart)):\(String(format: "%02d", secondPart))" let candidate = Candidate( text: timeExpression, diff --git a/Tests/KanaKanjiConverterModuleTests/ConverterTests/TimeExpressionTests.swift b/Tests/KanaKanjiConverterModuleTests/ConverterTests/TimeExpressionTests.swift index 862a32d..482d48e 100644 --- a/Tests/KanaKanjiConverterModuleTests/ConverterTests/TimeExpressionTests.swift +++ b/Tests/KanaKanjiConverterModuleTests/ConverterTests/TimeExpressionTests.swift @@ -17,7 +17,7 @@ final class TimeExpressionTests: XCTestCase { let input2 = makeDirectInput(direct: "1234") let input3 = makeDirectInput(direct: "999") let input4 = makeDirectInput(direct: "1260") - let input5 = makeDirectInput(direct: "1360") + let input5 = makeDirectInput(direct: "2440") let candidates1 = await converter.convertToTimeExpression(input1) let candidates2 = await converter.convertToTimeExpression(input2) @@ -34,9 +34,9 @@ final class TimeExpressionTests: XCTestCase { XCTAssertEqual(candidates3.count, 1) XCTAssertEqual(candidates3.first?.text, "9:99") - XCTAssertEqual(candidates4.count, 1) - XCTAssertEqual(candidates4.first?.text, "12:60") + XCTAssertEqual(candidates4.count, 0) - XCTAssertEqual(candidates5.count, 0) + XCTAssertEqual(candidates5.count, 1) + XCTAssertEqual(candidates5.first?.text, "24:40") } } From e8f9eb2f803e7543c559f907d762c827d9df57bc Mon Sep 17 00:00:00 2001 From: Miwa <63481257+ensan-hcl@users.noreply.github.com> Date: Sun, 30 Mar 2025 16:58:10 +0900 Subject: [PATCH 08/12] Update `testConvertToTimeExpression` to use `await` for KanaKanjiConverter initialization * Change `let converter = KanaKanjiConverter()` to `let converter = await KanaKanjiConverter()` --- .../ConverterTests/TimeExpressionTests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/KanaKanjiConverterModuleTests/ConverterTests/TimeExpressionTests.swift b/Tests/KanaKanjiConverterModuleTests/ConverterTests/TimeExpressionTests.swift index 482d48e..eefefa4 100644 --- a/Tests/KanaKanjiConverterModuleTests/ConverterTests/TimeExpressionTests.swift +++ b/Tests/KanaKanjiConverterModuleTests/ConverterTests/TimeExpressionTests.swift @@ -11,7 +11,7 @@ final class TimeExpressionTests: XCTestCase { } func testConvertToTimeExpression() async throws { - let converter = KanaKanjiConverter() + let converter = await KanaKanjiConverter() let input1 = makeDirectInput(direct: "123") let input2 = makeDirectInput(direct: "1234") From 38b84fe3e893c61a7aab48c4b4f20477128e05ba Mon Sep 17 00:00:00 2001 From: Miwa <63481257+ensan-hcl@users.noreply.github.com> Date: Sun, 30 Mar 2025 17:04:30 +0900 Subject: [PATCH 09/12] Add `convertToTimeExpression` function to convert numbers into time expressions * Add logic to handle 3-digit and 4-digit numbers * Check if the number is 3 digits with the first digit between 0-9 and the last two digits between 00-60 * Check if the number is 4 digits with the first two digits between 00-24 and the last two digits between 00-59 * Convert the number into a time expression in the format 'HH:MM' if conditions are met --- .../Converter/TimeExpression.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Sources/KanaKanjiConverterModule/Converter/TimeExpression.swift b/Sources/KanaKanjiConverterModule/Converter/TimeExpression.swift index b749a87..8a75692 100644 --- a/Sources/KanaKanjiConverterModule/Converter/TimeExpression.swift +++ b/Sources/KanaKanjiConverterModule/Converter/TimeExpression.swift @@ -4,8 +4,6 @@ extension KanaKanjiConverter { func convertToTimeExpression(_ inputData: ComposingText) -> [Candidate] { var candidates: [Candidate] = [] let numberString = inputData.convertTarget - let firstPart = Int(numberString.prefix(2))! - let secondPart = Int(numberString.suffix(2))! if numberString.count == 3 { let firstDigit = Int(numberString.prefix(1))! @@ -22,8 +20,10 @@ extension KanaKanjiConverter { candidates.append(candidate) } } else if numberString.count == 4 { - if (0...24).contains(firstPart) && (0...59).contains(secondPart) { - let timeExpression = "\(String(format: "%02d", firstPart)):\(String(format: "%02d", secondPart))" + let firstTwoDigits = Int(numberString.prefix(2))! + let lastTwoDigits = Int(numberString.suffix(2))! + if (0...24).contains(firstTwoDigits) && (0...59).contains(lastTwoDigits) { + let timeExpression = "\(String(format: "%02d", firstTwoDigits)):\(String(format: "%02d", lastTwoDigits))" let candidate = Candidate( text: timeExpression, value: -10, From a9971e690943e24fc14cc84cde60a06c9f578e89 Mon Sep 17 00:00:00 2001 From: Miwa <63481257+ensan-hcl@users.noreply.github.com> Date: Sun, 30 Mar 2025 17:05:48 +0900 Subject: [PATCH 10/12] Add digit check to `convertToTimeExpression` function * Check if all characters in the input string are digits before processing * Return an empty array if the input string contains non-digit characters --- .../KanaKanjiConverterModule/Converter/TimeExpression.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Sources/KanaKanjiConverterModule/Converter/TimeExpression.swift b/Sources/KanaKanjiConverterModule/Converter/TimeExpression.swift index 8a75692..36fc91b 100644 --- a/Sources/KanaKanjiConverterModule/Converter/TimeExpression.swift +++ b/Sources/KanaKanjiConverterModule/Converter/TimeExpression.swift @@ -5,6 +5,10 @@ extension KanaKanjiConverter { var candidates: [Candidate] = [] let numberString = inputData.convertTarget + // Check if all chars are digit. + if numberString.contains(where: { !$0.isDigit }) { + return [] + } if numberString.count == 3 { let firstDigit = Int(numberString.prefix(1))! let lastTwoDigits = Int(numberString.suffix(2))! From 858416289f591405cf93e43bd4af52198ee98ec1 Mon Sep 17 00:00:00 2001 From: Miwa <63481257+ensan-hcl@users.noreply.github.com> Date: Sun, 30 Mar 2025 17:09:02 +0900 Subject: [PATCH 11/12] Update digit check in `convertToTimeExpression` function * Check if characters are ASCII digits instead of just digits --- Sources/KanaKanjiConverterModule/Converter/TimeExpression.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/KanaKanjiConverterModule/Converter/TimeExpression.swift b/Sources/KanaKanjiConverterModule/Converter/TimeExpression.swift index 36fc91b..630f7cf 100644 --- a/Sources/KanaKanjiConverterModule/Converter/TimeExpression.swift +++ b/Sources/KanaKanjiConverterModule/Converter/TimeExpression.swift @@ -6,7 +6,7 @@ extension KanaKanjiConverter { let numberString = inputData.convertTarget // Check if all chars are digit. - if numberString.contains(where: { !$0.isDigit }) { + if numberString.contains(where: { !($0.isNumber && $0.isASCII) }) { return [] } if numberString.count == 3 { From eb040124e76bbdd0f90ee9931f93fcce46b0e187 Mon Sep 17 00:00:00 2001 From: Miwa / Ensan Date: Sun, 30 Mar 2025 17:20:07 +0900 Subject: [PATCH 12/12] fix: add more test cases --- .../ConverterTests/TimeExpressionTests.swift | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/Tests/KanaKanjiConverterModuleTests/ConverterTests/TimeExpressionTests.swift b/Tests/KanaKanjiConverterModuleTests/ConverterTests/TimeExpressionTests.swift index eefefa4..34b6a12 100644 --- a/Tests/KanaKanjiConverterModuleTests/ConverterTests/TimeExpressionTests.swift +++ b/Tests/KanaKanjiConverterModuleTests/ConverterTests/TimeExpressionTests.swift @@ -2,7 +2,7 @@ import XCTest @testable import KanaKanjiConverterModule final class TimeExpressionTests: XCTestCase { - func makeDirectInput(direct input: String) -> ComposingText { + private func makeDirectInput(direct input: String) -> ComposingText { ComposingText( convertTargetCursorPosition: input.count, input: input.map {.init(character: $0, inputStyle: .direct)}, @@ -18,12 +18,18 @@ final class TimeExpressionTests: XCTestCase { let input3 = makeDirectInput(direct: "999") let input4 = makeDirectInput(direct: "1260") let input5 = makeDirectInput(direct: "2440") + let input6 = makeDirectInput(direct: "") + let input7 = makeDirectInput(direct: "あいうえ") + let input8 = makeDirectInput(direct: "13122") let candidates1 = await converter.convertToTimeExpression(input1) let candidates2 = await converter.convertToTimeExpression(input2) let candidates3 = await converter.convertToTimeExpression(input3) let candidates4 = await converter.convertToTimeExpression(input4) let candidates5 = await converter.convertToTimeExpression(input5) + let candidates6 = await converter.convertToTimeExpression(input6) + let candidates7 = await converter.convertToTimeExpression(input7) + let candidates8 = await converter.convertToTimeExpression(input8) XCTAssertEqual(candidates1.count, 1) XCTAssertEqual(candidates1.first?.text, "1:23") @@ -31,12 +37,17 @@ final class TimeExpressionTests: XCTestCase { XCTAssertEqual(candidates2.count, 1) XCTAssertEqual(candidates2.first?.text, "12:34") - XCTAssertEqual(candidates3.count, 1) - XCTAssertEqual(candidates3.first?.text, "9:99") + XCTAssertEqual(candidates3.count, 0) XCTAssertEqual(candidates4.count, 0) XCTAssertEqual(candidates5.count, 1) XCTAssertEqual(candidates5.first?.text, "24:40") + + XCTAssertEqual(candidates6.count, 0) + + XCTAssertEqual(candidates7.count, 0) + + XCTAssertEqual(candidates8.count, 0) } }