fix(release): prepare 1.8 disclosures and licensed resources
Replace the unclear CLM source, align release and privacy disclosures, complete third-party notices, and keep the expanded test suites deterministic for release validation.
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
// AIUserSkillStoreTests.swift
|
||||
// OSGKeyboardTests
|
||||
|
||||
import XCTest
|
||||
@testable import OSGKeyboardShared
|
||||
|
||||
@MainActor
|
||||
final class AIUserSkillStoreTests: XCTestCase {
|
||||
private func makeDefaults() -> UserDefaults {
|
||||
let suite = "group.com.osgkeyboard.shared.tests.userSkills.\(UUID().uuidString)"
|
||||
let defaults = UserDefaults(suiteName: suite)!
|
||||
defaults.removePersistentDomain(forName: suite)
|
||||
return defaults
|
||||
}
|
||||
|
||||
func testChangingShortcutLinkDropsConfirmation() throws {
|
||||
let store = AIAgentSkillLayoutStore(defaults: makeDefaults())
|
||||
let firstURL = URL(string: "https://www.icloud.com/shortcuts/65bf33ba4206484ba78d582eaf1e9c44")!
|
||||
let secondURL = URL(string: "https://www.icloud.com/shortcuts/1f4afcf7ee22400cbf84e319d969aadf")!
|
||||
var skill = AIUserSkill(
|
||||
name: "Custom",
|
||||
prompt: "Do it",
|
||||
shortcutICloudURL: firstURL,
|
||||
shortcutName: "One"
|
||||
)
|
||||
try store.saveUserSkill(skill)
|
||||
XCTAssertEqual(store.confirmShortcutAndEnable(skill.id), .enabled)
|
||||
XCTAssertTrue(store.layout.isEnabled(skill.id))
|
||||
|
||||
skill.shortcutICloudURL = secondURL
|
||||
skill.shortcutName = "Two"
|
||||
try store.saveUserSkill(skill)
|
||||
XCTAssertFalse(store.layout.hasConfirmedShortcut(skill.id))
|
||||
XCTAssertFalse(store.layout.isEnabled(skill.id))
|
||||
}
|
||||
|
||||
func testRemovingShortcutLinkKeepsEnabledTextSkill() throws {
|
||||
let store = AIAgentSkillLayoutStore(defaults: makeDefaults())
|
||||
var skill = AIUserSkill(
|
||||
name: "Custom",
|
||||
prompt: "Do it",
|
||||
shortcutICloudURL: URL(
|
||||
string: "https://www.icloud.com/shortcuts/65bf33ba4206484ba78d582eaf1e9c44"
|
||||
),
|
||||
shortcutName: "Run Me"
|
||||
)
|
||||
try store.saveUserSkill(skill)
|
||||
XCTAssertEqual(store.confirmShortcutAndEnable(skill.id), .enabled)
|
||||
|
||||
skill.shortcutICloudURL = nil
|
||||
try store.saveUserSkill(skill)
|
||||
|
||||
XCTAssertTrue(store.layout.isEnabled(skill.id))
|
||||
XCTAssertFalse(store.layout.hasConfirmedShortcut(skill.id))
|
||||
XCTAssertEqual(store.userSkill(id: skill.id)?.asClipboardSkill().kind, .transform)
|
||||
}
|
||||
|
||||
func testAddingShortcutLinkDisablesTextSkillUntilConfirmed() throws {
|
||||
let store = AIAgentSkillLayoutStore(defaults: makeDefaults())
|
||||
var skill = AIUserSkill(
|
||||
name: "Custom",
|
||||
prompt: "Do it"
|
||||
)
|
||||
try store.saveUserSkill(skill)
|
||||
XCTAssertEqual(store.enable(skill.id), .enabled)
|
||||
|
||||
skill.shortcutICloudURL = URL(
|
||||
string: "https://www.icloud.com/shortcuts/65bf33ba4206484ba78d582eaf1e9c44"
|
||||
)
|
||||
skill.shortcutName = "Run Me"
|
||||
try store.saveUserSkill(skill)
|
||||
|
||||
XCTAssertFalse(store.layout.isEnabled(skill.id))
|
||||
XCTAssertFalse(store.layout.hasConfirmedShortcut(skill.id))
|
||||
XCTAssertEqual(store.enable(skill.id), .needsShortcut)
|
||||
}
|
||||
}
|
||||
@@ -236,75 +236,3 @@ final class AIUserSkillTests: XCTestCase {
|
||||
XCTAssertEqual(catalog.entries.count, 12)
|
||||
}
|
||||
}
|
||||
|
||||
@MainActor
|
||||
final class AIUserSkillStoreTests: XCTestCase {
|
||||
private func makeDefaults() -> UserDefaults {
|
||||
let suite = "group.com.osgkeyboard.shared.tests.userSkills.\(UUID().uuidString)"
|
||||
let defaults = UserDefaults(suiteName: suite)!
|
||||
defaults.removePersistentDomain(forName: suite)
|
||||
return defaults
|
||||
}
|
||||
|
||||
func testChangingShortcutLinkDropsConfirmation() throws {
|
||||
let store = AIAgentSkillLayoutStore(defaults: makeDefaults())
|
||||
let firstURL = URL(string: "https://www.icloud.com/shortcuts/65bf33ba4206484ba78d582eaf1e9c44")!
|
||||
let secondURL = URL(string: "https://www.icloud.com/shortcuts/1f4afcf7ee22400cbf84e319d969aadf")!
|
||||
var skill = AIUserSkill(
|
||||
name: "Custom",
|
||||
prompt: "Do it",
|
||||
shortcutICloudURL: firstURL,
|
||||
shortcutName: "One"
|
||||
)
|
||||
try store.saveUserSkill(skill)
|
||||
XCTAssertEqual(store.confirmShortcutAndEnable(skill.id), .enabled)
|
||||
XCTAssertTrue(store.layout.isEnabled(skill.id))
|
||||
|
||||
skill.shortcutICloudURL = secondURL
|
||||
skill.shortcutName = "Two"
|
||||
try store.saveUserSkill(skill)
|
||||
XCTAssertFalse(store.layout.hasConfirmedShortcut(skill.id))
|
||||
XCTAssertFalse(store.layout.isEnabled(skill.id))
|
||||
}
|
||||
|
||||
func testRemovingShortcutLinkKeepsEnabledTextSkill() throws {
|
||||
let store = AIAgentSkillLayoutStore(defaults: makeDefaults())
|
||||
var skill = AIUserSkill(
|
||||
name: "Custom",
|
||||
prompt: "Do it",
|
||||
shortcutICloudURL: URL(
|
||||
string: "https://www.icloud.com/shortcuts/65bf33ba4206484ba78d582eaf1e9c44"
|
||||
),
|
||||
shortcutName: "Run Me"
|
||||
)
|
||||
try store.saveUserSkill(skill)
|
||||
XCTAssertEqual(store.confirmShortcutAndEnable(skill.id), .enabled)
|
||||
|
||||
skill.shortcutICloudURL = nil
|
||||
try store.saveUserSkill(skill)
|
||||
|
||||
XCTAssertTrue(store.layout.isEnabled(skill.id))
|
||||
XCTAssertFalse(store.layout.hasConfirmedShortcut(skill.id))
|
||||
XCTAssertEqual(store.userSkill(id: skill.id)?.asClipboardSkill().kind, .transform)
|
||||
}
|
||||
|
||||
func testAddingShortcutLinkDisablesTextSkillUntilConfirmed() throws {
|
||||
let store = AIAgentSkillLayoutStore(defaults: makeDefaults())
|
||||
var skill = AIUserSkill(
|
||||
name: "Custom",
|
||||
prompt: "Do it"
|
||||
)
|
||||
try store.saveUserSkill(skill)
|
||||
XCTAssertEqual(store.enable(skill.id), .enabled)
|
||||
|
||||
skill.shortcutICloudURL = URL(
|
||||
string: "https://www.icloud.com/shortcuts/65bf33ba4206484ba78d582eaf1e9c44"
|
||||
)
|
||||
skill.shortcutName = "Run Me"
|
||||
try store.saveUserSkill(skill)
|
||||
|
||||
XCTAssertFalse(store.layout.isEnabled(skill.id))
|
||||
XCTAssertFalse(store.layout.hasConfirmedShortcut(skill.id))
|
||||
XCTAssertEqual(store.enable(skill.id), .needsShortcut)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,9 @@ final class LLMClientTests: XCTestCase {
|
||||
try? Keychain.deleteAPIKey(for: "qwen")
|
||||
try? Keychain.deleteAPIKey(for: "openai")
|
||||
try? Keychain.deleteAPIKey(for: "deepseek")
|
||||
try? Keychain.deleteAPIKey(for: "qwen", useICloudSync: true)
|
||||
try? Keychain.deleteAPIKey(for: "openai", useICloudSync: true)
|
||||
try? Keychain.deleteAPIKey(for: "deepseek", useICloudSync: true)
|
||||
StubURLProtocolStorage.config = nil
|
||||
StubURLProtocolStorage.delaySeconds = 0
|
||||
StubURLProtocolStorage.lastRequest = nil
|
||||
@@ -31,6 +34,9 @@ final class LLMClientTests: XCTestCase {
|
||||
try? Keychain.deleteAPIKey(for: "qwen")
|
||||
try? Keychain.deleteAPIKey(for: "openai")
|
||||
try? Keychain.deleteAPIKey(for: "deepseek")
|
||||
try? Keychain.deleteAPIKey(for: "qwen", useICloudSync: true)
|
||||
try? Keychain.deleteAPIKey(for: "openai", useICloudSync: true)
|
||||
try? Keychain.deleteAPIKey(for: "deepseek", useICloudSync: true)
|
||||
Keychain.resetTestMemoryStore()
|
||||
StubURLProtocolStorage.config = nil
|
||||
StubURLProtocolStorage.delaySeconds = 0
|
||||
@@ -65,6 +71,7 @@ final class LLMClientTests: XCTestCase {
|
||||
defer { defaults.removePersistentDomain(forName: suiteName) }
|
||||
|
||||
let config = ProviderConfig(defaults: defaults)
|
||||
config.apiKey = ""
|
||||
config.engineMode = "cloud"
|
||||
XCTAssertFalse(config.isConfigured)
|
||||
config.engineMode = "local"
|
||||
|
||||
@@ -13,9 +13,9 @@ final class LocalASRBiasAdapterTests: XCTestCase {
|
||||
let url = dir.appendingPathComponent("phrases.tsv")
|
||||
let tsv = """
|
||||
word\tpinyin\tsource\tweight
|
||||
SwiftUI\tswift ui\tcomputer_terms\t5
|
||||
Kubernetes\tku bo ne si\tcomputer_terms\t5
|
||||
一致性\tyi zhi xing\tcomputer_terms\t5
|
||||
SwiftUI\tswift ui\tai_tech_seed\t90
|
||||
Kubernetes\tku bo ne si\tai_tech_seed\t90
|
||||
一致性\tyi zhi xing\tai_tech_seed\t80
|
||||
"""
|
||||
try tsv.write(to: url, atomically: true, encoding: .utf8)
|
||||
addTeardownBlock {
|
||||
@@ -103,11 +103,12 @@ final class LocalASRBiasAdapterTests: XCTestCase {
|
||||
|
||||
func testBuiltinLexiconParsesTSV() {
|
||||
let terms = BuiltinLexiconIndex.parseTSV(
|
||||
"word\tpinyin\tsource\tweight\nFoo\tfoo\tcomputer_terms\t5\n"
|
||||
"word\tpinyin\tsource\tweight\nFoo\tfoo\tai_tech_seed\t80\n"
|
||||
)
|
||||
XCTAssertEqual(terms.count, 1)
|
||||
XCTAssertEqual(terms[0].word, "Foo")
|
||||
XCTAssertEqual(terms[0].weight, 5)
|
||||
XCTAssertEqual(terms[0].source, "ai_tech_seed")
|
||||
XCTAssertEqual(terms[0].weight, 80)
|
||||
}
|
||||
|
||||
func testPolishingServiceMergesDictionarySupplement() {
|
||||
|
||||
@@ -106,7 +106,7 @@ final class LocalASRModelCatalogTests: XCTestCase {
|
||||
func testMLXAdapterProducesPromptBiasNotHardHotwords() throws {
|
||||
let fixtureURL = FileManager.default.temporaryDirectory
|
||||
.appendingPathComponent("phrases-\(UUID().uuidString).tsv")
|
||||
try "word\tpinyin\tsource\tweight\nSwiftUI\tswift ui\tcomputer_terms\t5\n"
|
||||
try "word\tpinyin\tsource\tweight\nSwiftUI\tswift ui\tai_tech_seed\t90\n"
|
||||
.write(to: fixtureURL, atomically: true, encoding: .utf8)
|
||||
defer { try? FileManager.default.removeItem(at: fixtureURL) }
|
||||
|
||||
|
||||
@@ -5,16 +5,18 @@ import XCTest
|
||||
@testable import OSGKeyboard
|
||||
|
||||
final class OpenSourceLicenseCatalogTests: XCTestCase {
|
||||
func testIOSCatalogIncludesRimeAndDictionaryDataOnly() {
|
||||
func testIOSCatalogIncludesRimeDictionariesIconsAndCuratedSpeechData() {
|
||||
let entries = OpenSourceLicenseCatalog.entries(for: .iOS)
|
||||
let ids = Set(entries.map(\.id))
|
||||
|
||||
XCTAssertTrue(ids.contains("material-icons"))
|
||||
XCTAssertTrue(ids.contains("librime-static"))
|
||||
XCTAssertTrue(ids.contains("rime-pinyin-simp"))
|
||||
XCTAssertTrue(ids.contains("jieba"))
|
||||
XCTAssertTrue(ids.contains("phrase-pinyin-data"))
|
||||
XCTAssertTrue(ids.contains("pinyin-data"))
|
||||
XCTAssertTrue(ids.contains("english-typing-lexicon"))
|
||||
XCTAssertTrue(ids.contains("osg-ai-tech-lexicon"))
|
||||
XCTAssertFalse(ids.contains("mlx-audio-swift"))
|
||||
}
|
||||
|
||||
@@ -22,7 +24,15 @@ final class OpenSourceLicenseCatalogTests: XCTestCase {
|
||||
let entries = OpenSourceLicenseCatalog.entries(for: .macOS)
|
||||
let ids = Set(entries.map(\.id))
|
||||
|
||||
XCTAssertEqual(ids, ["mlx-audio-swift"])
|
||||
XCTAssertEqual(ids, [
|
||||
"mlx-audio-swift",
|
||||
"mlx-swift",
|
||||
"mlx-swift-lm",
|
||||
"osg-ai-tech-lexicon",
|
||||
"qwen3-asr-mlx",
|
||||
"swift-huggingface",
|
||||
"swift-transformers",
|
||||
])
|
||||
}
|
||||
|
||||
func testBundledDictionaryLicensesKeepCopyrightNotices() throws {
|
||||
@@ -46,4 +56,39 @@ final class OpenSourceLicenseCatalogTests: XCTestCase {
|
||||
XCTAssertTrue(entry.licenseText.contains("leveldb.txt"))
|
||||
XCTAssertTrue(entry.licenseText.contains("yaml-cpp.txt"))
|
||||
}
|
||||
|
||||
func testMaterialIconsUseGoogleAttributionAndFullApacheLicense() throws {
|
||||
let entry = try XCTUnwrap(
|
||||
OpenSourceLicenseCatalog.entries(for: .iOS)
|
||||
.first { $0.id == "material-icons" }
|
||||
)
|
||||
|
||||
XCTAssertTrue(entry.licenseText.contains("Copyright 2014 Google LLC"))
|
||||
XCTAssertTrue(entry.licenseText.contains("TERMS AND CONDITIONS FOR USE"))
|
||||
}
|
||||
|
||||
func testMacMLXAndQwenNoticesContainUpstreamAttribution() throws {
|
||||
let entries = OpenSourceLicenseCatalog.entries(for: .macOS)
|
||||
let mlxAudio = try XCTUnwrap(entries.first { $0.id == "mlx-audio-swift" })
|
||||
let mlxSwift = try XCTUnwrap(entries.first { $0.id == "mlx-swift" })
|
||||
let transformers = try XCTUnwrap(entries.first { $0.id == "swift-transformers" })
|
||||
let qwen = try XCTUnwrap(entries.first { $0.id == "qwen3-asr-mlx" })
|
||||
|
||||
XCTAssertTrue(mlxAudio.licenseText.contains("Copyright (c) 2025 Prince Canuma"))
|
||||
XCTAssertTrue(mlxSwift.licenseText.contains("Copyright (c) 2023 ml-explore"))
|
||||
XCTAssertTrue(transformers.licenseText.contains("Copyright 2022 Hugging Face SAS."))
|
||||
XCTAssertTrue(qwen.licenseText.contains("Qwen3-ASR-0.6B-4bit"))
|
||||
XCTAssertTrue(qwen.licenseText.contains("Apache License"))
|
||||
}
|
||||
|
||||
func testCuratedLexiconNoticeDoesNotRelicenseApplication() throws {
|
||||
let entry = try XCTUnwrap(
|
||||
OpenSourceLicenseCatalog.entries(for: .iOS)
|
||||
.first { $0.id == "osg-ai-tech-lexicon" }
|
||||
)
|
||||
|
||||
XCTAssertTrue(entry.licenseText.contains("data subset"))
|
||||
XCTAssertTrue(entry.licenseText.contains("source-available"))
|
||||
XCTAssertTrue(entry.licenseText.contains("not distributed under the MIT License"))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user