vChewing-macOS/Packages/vChewing_LangModelAssembly/Tests/LangModelAssemblyTests/LMUserOverrideTests.swift

105 lines
6.2 KiB
Swift

//// (c) 2021 and onwards The vChewing Project (MIT-NTL License).
// ====================
// This code is released under the MIT license (SPDX-License-Identifier: MIT)
// ... with NTL restriction stating that:
// No trademark license is granted to use the trade names, trademarks, service
// marks, or product names of Contributor, except as required to fulfill notice
// requirements defined in MIT License.
import Foundation
import XCTest
@testable import LangModelAssembly
private let nowTimeStamp: Double = 114_514 * 10000
private let capacity = 5
private let halfLife: Double = 5400
private let nullURL = URL(fileURLWithPath: "/dev/null")
final class LMUserOverrideTests: XCTestCase {
private func observe(who uom: LMAssembly.LMUserOverride, key: String, candidate: String, timestamp stamp: Double) {
uom.doObservation(key: key, candidate: candidate, timestamp: stamp, forceHighScoreOverride: false, saveCallback: {})
}
func testUOM_1_BasicOps() throws {
let uom = LMAssembly.LMUserOverride(capacity: capacity, decayConstant: Double(halfLife), dataURL: nullURL)
let key = "((ㄕㄣˊ-ㄌㄧˇ-ㄌㄧㄥˊ-ㄏㄨㄚˊ,神里綾華),(ㄉㄜ˙,的),ㄍㄡˇ)"
let headReading = "ㄍㄡˇ"
let expectedSuggestion = ""
observe(who: uom, key: key, candidate: expectedSuggestion, timestamp: nowTimeStamp)
var suggested = uom.getSuggestion(key: key, timestamp: nowTimeStamp, headReading: headReading)
XCTAssertEqual(Set(suggested.candidates.map(\.1.value)).first ?? "", expectedSuggestion)
var i = 0
while !suggested.candidates.isEmpty {
suggested = uom.getSuggestion(key: key, timestamp: nowTimeStamp + (halfLife * Double(i)), headReading: headReading)
let suggestedCandidates = suggested.candidates
if suggestedCandidates.isEmpty { print(i) }
if i >= 21 {
XCTAssertNotEqual(Set(suggested.candidates.map(\.1.value)).first ?? "", expectedSuggestion, i.description)
XCTAssert(suggested.candidates.isEmpty)
} else {
XCTAssertEqual(Set(suggested.candidates.map(\.1.value)).first ?? "", expectedSuggestion, i.description)
}
i += 1
}
}
func testUOM_2_NewestAgainstRepeatedlyUsed() throws {
let uom = LMAssembly.LMUserOverride(capacity: capacity, decayConstant: Double(halfLife), dataURL: nullURL)
let key = "((ㄕㄣˊ-ㄌㄧˇ-ㄌㄧㄥˊ-ㄏㄨㄚˊ,神里綾華),(ㄉㄜ˙,的),ㄍㄡˇ)"
let headReading = "ㄍㄡˇ"
let valRepeatedlyUsed = "" //
let valNewest = "" //
let stamps: [Double] = [0, 0.5, 2, 2.5, 4, 4.5, 5.3].map { nowTimeStamp + halfLife * $0 }
stamps.forEach { stamp in
observe(who: uom, key: key, candidate: valRepeatedlyUsed, timestamp: stamp)
}
var suggested = uom.getSuggestion(key: key, timestamp: nowTimeStamp, headReading: headReading)
XCTAssertEqual(Set(suggested.candidates.map(\.1.value)).first ?? "", valRepeatedlyUsed)
[6.0, 18.0, 23.0].forEach { i in
suggested = uom.getSuggestion(key: key, timestamp: nowTimeStamp + halfLife * Double(i), headReading: headReading)
XCTAssertEqual(Set(suggested.candidates.map(\.1.value)).first ?? "", valRepeatedlyUsed, i.description)
}
//
observe(who: uom, key: key, candidate: valNewest, timestamp: nowTimeStamp + halfLife * 23.4)
suggested = uom.getSuggestion(key: key, timestamp: nowTimeStamp + halfLife * 26, headReading: headReading)
XCTAssertEqual(Set(suggested.candidates.map(\.1.value)).first ?? "", valNewest)
suggested = uom.getSuggestion(key: key, timestamp: nowTimeStamp + halfLife * 50, headReading: headReading)
XCTAssertNotEqual(Set(suggested.candidates.map(\.1.value)).first ?? "", valNewest)
XCTAssert(suggested.candidates.isEmpty)
}
func testUOM_3_LRUTable() throws {
let a = (key: "((ㄕㄣˊ-ㄌㄧˇ-ㄌㄧㄥˊ-ㄏㄨㄚˊ,神里綾華),(ㄉㄜ˙,的),ㄍㄡˇ)", value: "", head: "ㄍㄡˇ")
let b = (key: "((ㄆㄞˋ-ㄇㄥˊ,派蒙),(ㄉㄜ˙,的),ㄐㄧㄤˇ-ㄐㄧㄣ)", value: "伙食費", head: "ㄏㄨㄛˇ-ㄕˊ-ㄈㄟˋ")
let c = (key: "((ㄍㄨㄛˊ-ㄅㄥ,國崩),(ㄉㄜ˙,的),ㄇㄠˋ-ㄗ˙)", value: "帽子", head: "ㄇㄠˋ-ㄗ˙")
let d = (key: "((ㄌㄟˊ-ㄉㄧㄢˋ-ㄐㄧㄤ-ㄐㄩㄣ,雷電將軍),(ㄉㄜ˙,的),ㄐㄧㄠˇ-ㄔㄡˋ)", value: "腳臭", head: "ㄐㄧㄠˇ-ㄔㄡˋ")
let uom = LMAssembly.LMUserOverride(capacity: 2, decayConstant: Double(halfLife), dataURL: nullURL)
observe(who: uom, key: a.key, candidate: a.value, timestamp: nowTimeStamp)
observe(who: uom, key: b.key, candidate: b.value, timestamp: nowTimeStamp + halfLife * 1)
observe(who: uom, key: c.key, candidate: c.value, timestamp: nowTimeStamp + halfLife * 2)
// C is in the list.
var suggested = uom.getSuggestion(key: c.key, timestamp: nowTimeStamp + halfLife * 3, headReading: c.head)
XCTAssertEqual(Set(suggested.candidates.map(\.1.value)).first ?? "", c.value)
// B is in the list.
suggested = uom.getSuggestion(key: b.key, timestamp: nowTimeStamp + halfLife * 3.5, headReading: b.head)
XCTAssertEqual(Set(suggested.candidates.map(\.1.value)).first ?? "", b.value)
// A is purged.
suggested = uom.getSuggestion(key: a.key, timestamp: nowTimeStamp + halfLife * 4, headReading: a.head)
XCTAssertNotEqual(Set(suggested.candidates.map(\.1.value)).first ?? "", a.value)
XCTAssert(suggested.candidates.isEmpty)
// Observe a new pair (D).
observe(who: uom, key: d.key, candidate: d.value, timestamp: nowTimeStamp + halfLife * 4.5)
// D is in the list.
suggested = uom.getSuggestion(key: d.key, timestamp: nowTimeStamp + halfLife * 5, headReading: d.head)
XCTAssertEqual(Set(suggested.candidates.map(\.1.value)).first ?? "", d.value)
// C is in the list.
suggested = uom.getSuggestion(key: c.key, timestamp: nowTimeStamp + halfLife * 5.5, headReading: c.head)
XCTAssertEqual(Set(suggested.candidates.map(\.1.value)).first ?? "", c.value)
// B is purged.
suggested = uom.getSuggestion(key: b.key, timestamp: nowTimeStamp + halfLife * 6, headReading: b.head)
XCTAssertNotEqual(Set(suggested.candidates.map(\.1.value)).first ?? "", b.value)
XCTAssert(suggested.candidates.isEmpty)
}
}