170 lines
7.3 KiB
Swift
170 lines
7.3 KiB
Swift
// ThemeTests.swift
|
||
// Unit tests for TerminalTheme, ThemeColor, ThemeStore, and FontStore.
|
||
//
|
||
// These tests exercise built-in theme validity and store selection logic.
|
||
// No UIKit rendering or simulator is required for the pure-struct tests;
|
||
// ThemeStore / FontStore tests require @MainActor and a run loop.
|
||
|
||
import XCTest
|
||
@testable import piRemote
|
||
|
||
final class ThemeTests: XCTestCase {
|
||
|
||
// =========================================================================
|
||
// MARK: 1. TerminalTheme — built-in theme invariants
|
||
// =========================================================================
|
||
|
||
func testDark_hasExactly16AnsiColors() {
|
||
XCTAssertEqual(TerminalTheme.dark.ansiColors.count, 16,
|
||
".dark must have exactly 16 ANSI color entries")
|
||
}
|
||
|
||
func testGitHub_hasExactly16AnsiColors() {
|
||
XCTAssertEqual(TerminalTheme.github.ansiColors.count, 16,
|
||
".github must have exactly 16 ANSI color entries")
|
||
}
|
||
|
||
// =========================================================================
|
||
// MARK: 2. ThemeColor values are in the 0–255 range
|
||
// =========================================================================
|
||
// ThemeColor stores UInt8 components, so they are structurally bounded to
|
||
// 0–255. These tests make the contract explicit and guard against future
|
||
// refactors that might widen the type.
|
||
|
||
func testDark_allAnsiColorComponents_inRange() {
|
||
for (index, color) in TerminalTheme.dark.ansiColors.enumerated() {
|
||
assertColorInRange(color, label: "dark[\(index)]")
|
||
}
|
||
assertColorInRange(TerminalTheme.dark.foreground, label: "dark.foreground")
|
||
assertColorInRange(TerminalTheme.dark.background, label: "dark.background")
|
||
assertColorInRange(TerminalTheme.dark.cursor, label: "dark.cursor")
|
||
}
|
||
|
||
func testGitHub_allAnsiColorComponents_inRange() {
|
||
for (index, color) in TerminalTheme.github.ansiColors.enumerated() {
|
||
assertColorInRange(color, label: "github[\(index)]")
|
||
}
|
||
assertColorInRange(TerminalTheme.github.foreground, label: "github.foreground")
|
||
assertColorInRange(TerminalTheme.github.background, label: "github.background")
|
||
assertColorInRange(TerminalTheme.github.cursor, label: "github.cursor")
|
||
}
|
||
|
||
private func assertColorInRange(_ color: ThemeColor, label: String) {
|
||
// UInt8 is intrinsically 0–255; the assertions below make the expectation readable.
|
||
XCTAssertLessThanOrEqual(color.r, 255, "\(label).r out of range")
|
||
XCTAssertLessThanOrEqual(color.g, 255, "\(label).g out of range")
|
||
XCTAssertLessThanOrEqual(color.b, 255, "\(label).b out of range")
|
||
}
|
||
|
||
// =========================================================================
|
||
// MARK: 3. .dark and .github are distinct themes
|
||
// =========================================================================
|
||
|
||
func testDarkAndGitHub_haveDistinctIds() {
|
||
XCTAssertNotEqual(TerminalTheme.dark.id, TerminalTheme.github.id,
|
||
"Built-in themes must have unique IDs")
|
||
}
|
||
|
||
func testDarkAndGitHub_haveDistinctBackgrounds() {
|
||
// "Dark (Hacker)" has a pure-black background; GitHub Dark does not.
|
||
XCTAssertNotEqual(TerminalTheme.dark.background, TerminalTheme.github.background,
|
||
"Background colors must differ between dark and github themes")
|
||
}
|
||
|
||
func testDarkAndGitHub_areNotEqual() {
|
||
XCTAssertNotEqual(TerminalTheme.dark, TerminalTheme.github,
|
||
".dark and .github must be distinct themes")
|
||
}
|
||
|
||
// =========================================================================
|
||
// MARK: 4. TerminalTheme identities
|
||
// =========================================================================
|
||
|
||
func testDark_hasExpectedId() {
|
||
XCTAssertEqual(TerminalTheme.dark.id, "dark")
|
||
}
|
||
|
||
func testGitHub_hasExpectedId() {
|
||
XCTAssertEqual(TerminalTheme.github.id, "github")
|
||
}
|
||
|
||
func testDark_backgroundIsBlack() {
|
||
let bg = TerminalTheme.dark.background
|
||
XCTAssertEqual(bg.r, 0x00, "Dark background R must be 0x00")
|
||
XCTAssertEqual(bg.g, 0x00, "Dark background G must be 0x00")
|
||
XCTAssertEqual(bg.b, 0x00, "Dark background B must be 0x00")
|
||
}
|
||
|
||
func testGitHub_backgroundMatchesSpec() {
|
||
// GitHub Dark background: #0d1117
|
||
let bg = TerminalTheme.github.background
|
||
XCTAssertEqual(bg.r, 0x0D, "GitHub background R must be 0x0D")
|
||
XCTAssertEqual(bg.g, 0x11, "GitHub background G must be 0x11")
|
||
XCTAssertEqual(bg.b, 0x17, "GitHub background B must be 0x17")
|
||
}
|
||
|
||
// =========================================================================
|
||
// MARK: 5. TerminalTheme.toSwiftTermColor conversion
|
||
// =========================================================================
|
||
|
||
func testToSwiftTermColor_white_mapsTo65535() {
|
||
// 255 × 257 = 65535 (= UInt16.max)
|
||
let white = ThemeColor(r: 255, g: 255, b: 255)
|
||
let stColor = TerminalTheme.dark.toSwiftTermColor(white)
|
||
XCTAssertEqual(stColor.red, 65535)
|
||
XCTAssertEqual(stColor.green, 65535)
|
||
XCTAssertEqual(stColor.blue, 65535)
|
||
}
|
||
|
||
func testToSwiftTermColor_black_mapsToZero() {
|
||
let black = ThemeColor(r: 0, g: 0, b: 0)
|
||
let stColor = TerminalTheme.dark.toSwiftTermColor(black)
|
||
XCTAssertEqual(stColor.red, 0)
|
||
XCTAssertEqual(stColor.green, 0)
|
||
XCTAssertEqual(stColor.blue, 0)
|
||
}
|
||
|
||
func testSwiftTermAnsiColors_has16Entries() {
|
||
XCTAssertEqual(TerminalTheme.dark.swiftTermAnsiColors.count, 16)
|
||
XCTAssertEqual(TerminalTheme.github.swiftTermAnsiColors.count, 16)
|
||
}
|
||
|
||
// =========================================================================
|
||
// MARK: 6. ThemeStore — selection and persistence
|
||
// =========================================================================
|
||
|
||
@MainActor
|
||
func testThemeStore_available_containsBothBuiltins() {
|
||
let store = ThemeStore.shared
|
||
let ids = store.available.map(\.id)
|
||
XCTAssertTrue(ids.contains("dark"), "Available themes must include 'dark'")
|
||
XCTAssertTrue(ids.contains("github"), "Available themes must include 'github'")
|
||
}
|
||
|
||
@MainActor
|
||
func testThemeStore_select_updatesCurrent() {
|
||
let store = ThemeStore.shared
|
||
let before = store.current
|
||
|
||
// Select whichever theme is NOT currently active.
|
||
let next = store.available.first(where: { $0.id != before.id })!
|
||
store.select(next)
|
||
XCTAssertEqual(store.current.id, next.id,
|
||
"select(_:) must update current immediately")
|
||
|
||
// Restore original selection so we don't bleed state into other tests.
|
||
store.select(before)
|
||
}
|
||
|
||
// =========================================================================
|
||
// MARK: 7. TerminalTheme Codable round-trip
|
||
// =========================================================================
|
||
|
||
func testTerminalTheme_codable_roundTrips() throws {
|
||
let theme = TerminalTheme.dark
|
||
let data = try JSONEncoder().encode(theme)
|
||
let loaded = try JSONDecoder().decode(TerminalTheme.self, from: data)
|
||
XCTAssertEqual(loaded, theme, "TerminalTheme must survive a JSON round-trip")
|
||
}
|
||
}
|