diff --git a/Sources/KanaKanjiConverterModule/DicdataStore/DicdataStore.swift b/Sources/KanaKanjiConverterModule/DicdataStore/DicdataStore.swift index c08944b..a8f7c28 100644 --- a/Sources/KanaKanjiConverterModule/DicdataStore/DicdataStore.swift +++ b/Sources/KanaKanjiConverterModule/DicdataStore/DicdataStore.swift @@ -139,7 +139,7 @@ public final class DicdataStore { } else { self.requestOptions = value } - let shouldReset = self.learningManager.setRequestOptions(options: value) + let shouldReset = self.learningManager.setRequestOptions(value) if shouldReset { self.reloadMemory() } diff --git a/Sources/KanaKanjiConverterModule/DicdataStore/LearningMemory.swift b/Sources/KanaKanjiConverterModule/DicdataStore/LearningMemory.swift index faed0de..5bc14c7 100644 --- a/Sources/KanaKanjiConverterModule/DicdataStore/LearningMemory.swift +++ b/Sources/KanaKanjiConverterModule/DicdataStore/LearningMemory.swift @@ -653,47 +653,49 @@ final class LearningManager { (!self.memoryCollapsed) && self.options.learningType.needUsingMemory } - init() { - self.memoryCollapsed = LongTermLearningMemory.memoryCollapsed(directoryURL: self.options.memoryDirectoryURL) - if self.memoryCollapsed && options.learningType.needUsingMemory { + init() {} + + /// - Returns: Whether cache should be reseted or not. + func setRequestOptions(_ newOptions: ConvertRequestOptions) -> Bool { + // 更新の必要がなければ何もしない + if !newOptions.learningType.needUsingMemory { + self.options = newOptions + return false + } + // 学習の壊れ状態を確認 + self.memoryCollapsed = LongTermLearningMemory.memoryCollapsed(directoryURL: newOptions.memoryDirectoryURL) + if self.memoryCollapsed && newOptions.learningType.needUsingMemory { do { try LongTermLearningMemory.merge( tempTrie: TemporalLearningMemoryTrie(), - directoryURL: self.options.memoryDirectoryURL, - maxMemoryCount: options.maxMemoryCount, + directoryURL: newOptions.memoryDirectoryURL, + maxMemoryCount: newOptions.maxMemoryCount, char2UInt8: char2UInt8 ) } catch { - debug("LearningManager init: automatic merge failed", error) + debug(#file, #function, "automatic merge failed", error) } - self.memoryCollapsed = LongTermLearningMemory.memoryCollapsed(directoryURL: self.options.memoryDirectoryURL) + self.memoryCollapsed = LongTermLearningMemory.memoryCollapsed(directoryURL: newOptions.memoryDirectoryURL) } - if memoryCollapsed { + if self.memoryCollapsed { // 学習データが壊れている状態であることを警告する - debug("LearningManager init: Memory Collapsed") + debug(#file, #function, "LearningManager init: Memory Collapsed") } - if !options.learningType.needUsingMemory { - return - } - Self.updateChar2Int8(bundleURL: options.dictionaryResourceURL, target: &char2UInt8) - } - - /// - Returns: Whether cache should be reseted or not. - func setRequestOptions(options: ConvertRequestOptions) -> Bool { // 変更があったら`char2Int8`を読み込み直す - if options.dictionaryResourceURL != self.options.dictionaryResourceURL { - Self.updateChar2Int8(bundleURL: options.dictionaryResourceURL, target: &char2UInt8) + if newOptions.dictionaryResourceURL != self.options.dictionaryResourceURL { + Self.updateChar2Int8(bundleURL: newOptions.dictionaryResourceURL, target: &char2UInt8) } - self.options = options + // ここで更新 + self.options = newOptions - switch options.learningType { + switch self.options.learningType { case .inputAndOutput, .onlyOutput: break case .nothing: self.temporaryMemory = TemporalLearningMemoryTrie() } // リセットチェックも実施 - if options.shouldResetMemory { + if self.options.shouldResetMemory { self.reset() self.options.shouldResetMemory = false return true diff --git a/Tests/KanaKanjiConverterModuleTests/LearningMemoryTests.swift b/Tests/KanaKanjiConverterModuleTests/LearningMemoryTests.swift index c6f9dd5..d3478f6 100644 --- a/Tests/KanaKanjiConverterModuleTests/LearningMemoryTests.swift +++ b/Tests/KanaKanjiConverterModuleTests/LearningMemoryTests.swift @@ -11,15 +11,36 @@ import XCTest final class LearningMemoryTests: XCTestCase { static let resourceURL = Bundle.module.resourceURL!.appendingPathComponent("DictionaryMock", isDirectory: true) + private func getOptionsForMemoryTest(memoryDirectoryURL: URL) -> ConvertRequestOptions { + var options = ConvertRequestOptions.default + options.memoryDirectoryURL = memoryDirectoryURL + options.dictionaryResourceURL = Self.resourceURL + options.learningType = .inputAndOutput + options.maxMemoryCount = 32 + return options + } + func testPauseFileIsClearedOnInit() throws { - let dir = ConvertRequestOptions.default.memoryDirectoryURL - try? FileManager.default.createDirectory(at: dir, withIntermediateDirectories: true) + let dir = FileManager.default.temporaryDirectory.appendingPathComponent("LearningMemoryTest-\(UUID().uuidString)", isDirectory: true) + try FileManager.default.createDirectory(at: dir, withIntermediateDirectories: true) + defer { try? FileManager.default.removeItem(at: dir) } + + let options = self.getOptionsForMemoryTest(memoryDirectoryURL: dir) + let manager = LearningManager() + _ = manager.setRequestOptions(options) + + let element = DicdataElement(word: "テスト", ruby: "テスト", cid: CIDData.一般名詞.cid, mid: MIDData.一般.mid, value: -10) + manager.update(data: [element]) + manager.save() + + // ポーズファイルを設置 let pauseURL = dir.appendingPathComponent(".pause", isDirectory: false) FileManager.default.createFile(atPath: pauseURL.path, contents: Data()) XCTAssertTrue(LongTermLearningMemory.memoryCollapsed(directoryURL: dir)) - // `init`に副作用がある - _ = LearningManager() + // ここで副作用が発生 + _ = manager.setRequestOptions(options) + // 学習の破壊状態が回復されていることを確認 XCTAssertFalse(LongTermLearningMemory.memoryCollapsed(directoryURL: dir)) try? FileManager.default.removeItem(at: pauseURL) @@ -30,13 +51,9 @@ final class LearningMemoryTests: XCTestCase { try FileManager.default.createDirectory(at: dir, withIntermediateDirectories: true) defer { try? FileManager.default.removeItem(at: dir) } + let options = self.getOptionsForMemoryTest(memoryDirectoryURL: dir) let manager = LearningManager() - var options = ConvertRequestOptions.default - options.dictionaryResourceURL = Self.resourceURL - options.memoryDirectoryURL = dir - options.learningType = .inputAndOutput - options.maxMemoryCount = 32 - _ = manager.setRequestOptions(options: options) + _ = manager.setRequestOptions(options) let element = DicdataElement(word: "テスト", ruby: "テスト", cid: CIDData.一般名詞.cid, mid: MIDData.一般.mid, value: -10) manager.update(data: [element])