@testable import KanaKanjiConverterModule 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 = 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)) // ここで副作用が発生 _ = manager.setRequestOptions(options) // 学習の破壊状態が回復されていることを確認 XCTAssertFalse(LongTermLearningMemory.memoryCollapsed(directoryURL: dir)) try? FileManager.default.removeItem(at: pauseURL) } func testMemoryFilesCreateAndRemove() throws { 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 files = try FileManager.default.contentsOfDirectory(at: dir, includingPropertiesForKeys: nil) XCTAssertTrue(files.contains { $0.lastPathComponent == "memory.louds" }) XCTAssertTrue(files.contains { $0.lastPathComponent == "memory.loudschars2" }) XCTAssertTrue(files.contains { $0.lastPathComponent == "memory.memorymetadata" }) XCTAssertTrue(files.contains { $0.lastPathComponent.hasSuffix(".loudstxt3") }) manager.reset() let filesAfter = try FileManager.default.contentsOfDirectory(at: dir, includingPropertiesForKeys: nil) XCTAssertTrue(filesAfter.isEmpty) } func testForgetMemory() throws { let dir = FileManager.default.temporaryDirectory.appendingPathComponent("LearningManagerPersistence-\(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 dicdataStore = DicdataStore(requestOptions: options) dicdataStore.sendToDicdataStore(.setRequestOptions(options)) let charIDs = "テスト".map { dicdataStore.character2charId($0) } let indices = dicdataStore.perfectMatchingSearch(query: "memory", charIDs: charIDs) let dicdata = dicdataStore.getDicdataFromLoudstxt3(identifier: "memory", indices: indices) XCTAssertFalse(dicdata.isEmpty) XCTAssertTrue(dicdata.contains { $0.word == element.word && $0.ruby == element.ruby }) dicdataStore.sendToDicdataStore( .forgetMemory( Candidate( text: element.word, value: element.value(), composingCount: .inputCount(3), lastMid: element.mid, data: [element] ) ) ) let indices2 = dicdataStore.perfectMatchingSearch(query: "memory", charIDs: charIDs) let dicdata2 = dicdataStore.getDicdataFromLoudstxt3(identifier: "memory", indices: indices2) XCTAssertFalse(dicdata2.contains { $0.word == element.word && $0.ruby == element.ruby }) } func testCoarseForgetMemory() throws { // ForgetMemoryは「粗い」チェックを行うため、品詞が異なっていても同時に忘却される let dir = FileManager.default.temporaryDirectory.appendingPathComponent("LearningManagerPersistence-\(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]) let differentCidElement = DicdataElement(word: "テスト", ruby: "テスト", cid: CIDData.固有名詞.cid, mid: MIDData.一般.mid, value: -10) manager.update(data: [differentCidElement]) manager.save() let dicdataStore = DicdataStore(requestOptions: options) dicdataStore.sendToDicdataStore(.setRequestOptions(options)) let charIDs = "テスト".map { dicdataStore.character2charId($0) } let indices = dicdataStore.perfectMatchingSearch(query: "memory", charIDs: charIDs) let dicdata = dicdataStore.getDicdataFromLoudstxt3(identifier: "memory", indices: indices) XCTAssertFalse(dicdata.isEmpty) XCTAssertEqual(dicdata.count { $0.word == element.word && $0.ruby == element.ruby }, 2) dicdataStore.sendToDicdataStore( .forgetMemory( Candidate( text: element.word, value: element.value(), composingCount: .inputCount(3), lastMid: element.mid, data: [element] ) ) ) let indices2 = dicdataStore.perfectMatchingSearch(query: "memory", charIDs: charIDs) let dicdata2 = dicdataStore.getDicdataFromLoudstxt3(identifier: "memory", indices: indices2) XCTAssertFalse(dicdata2.contains { $0.word == element.word && $0.ruby == element.ruby }) } }