Apply SwiftFormat and fix all SwiftLint violations

- Run SwiftFormat on all Swift files for consistent formatting
- Fix all critical SwiftLint violations:
  * Replace count > 0 with \!isEmpty
  * Use descriptive variable names instead of i, x, y
  * Replace % operator with isMultiple(of:)
  * Fix force try violations
  * Use trailing closure syntax
  * Replace for-if patterns with for-where
  * Fix line length violations
  * Use Data(_:) instead of .data(using:)\!
- Ensure zero SwiftLint errors for clean code quality

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Peter Steinberger 2025-06-08 00:18:23 +01:00
parent 45f087496a
commit e894210dbd
14 changed files with 865 additions and 832 deletions

View file

@ -41,22 +41,20 @@ struct AppsSubcommand: ParsableCommand {
} }
private func handleError(_ error: Error) { private func handleError(_ error: Error) {
let captureError: CaptureError let captureError: CaptureError = if let err = error as? CaptureError {
if let err = error as? CaptureError { err
captureError = err
} else { } else {
captureError = .unknownError(error.localizedDescription) .unknownError(error.localizedDescription)
} }
if jsonOutput { if jsonOutput {
let code: ErrorCode let code: ErrorCode = switch captureError {
switch captureError {
case .screenRecordingPermissionDenied: case .screenRecordingPermissionDenied:
code = .PERMISSION_ERROR_SCREEN_RECORDING .PERMISSION_ERROR_SCREEN_RECORDING
case .accessibilityPermissionDenied: case .accessibilityPermissionDenied:
code = .PERMISSION_ERROR_ACCESSIBILITY .PERMISSION_ERROR_ACCESSIBILITY
default: default:
code = .INTERNAL_SWIFT_ERROR .INTERNAL_SWIFT_ERROR
} }
outputError( outputError(
message: captureError.localizedDescription, message: captureError.localizedDescription,
@ -142,24 +140,22 @@ struct WindowsSubcommand: ParsableCommand {
} }
private func handleError(_ error: Error) { private func handleError(_ error: Error) {
let captureError: CaptureError let captureError: CaptureError = if let err = error as? CaptureError {
if let err = error as? CaptureError { err
captureError = err
} else { } else {
captureError = .unknownError(error.localizedDescription) .unknownError(error.localizedDescription)
} }
if jsonOutput { if jsonOutput {
let code: ErrorCode let code: ErrorCode = switch captureError {
switch captureError {
case .screenRecordingPermissionDenied: case .screenRecordingPermissionDenied:
code = .PERMISSION_ERROR_SCREEN_RECORDING .PERMISSION_ERROR_SCREEN_RECORDING
case .accessibilityPermissionDenied: case .accessibilityPermissionDenied:
code = .PERMISSION_ERROR_ACCESSIBILITY .PERMISSION_ERROR_ACCESSIBILITY
case .appNotFound: case .appNotFound:
code = .APP_NOT_FOUND .APP_NOT_FOUND
default: default:
code = .INTERNAL_SWIFT_ERROR .INTERNAL_SWIFT_ERROR
} }
outputError( outputError(
message: captureError.localizedDescription, message: captureError.localizedDescription,

View file

@ -56,8 +56,8 @@ class Logger {
} }
func getDebugLogs() -> [String] { func getDebugLogs() -> [String] {
return queue.sync { queue.sync {
return self.debugLogs self.debugLogs
} }
} }

View file

@ -118,9 +118,11 @@ enum CaptureError: Error, LocalizedError {
case .noDisplaysAvailable: case .noDisplaysAvailable:
"No displays available for capture." "No displays available for capture."
case .screenRecordingPermissionDenied: case .screenRecordingPermissionDenied:
"Screen recording permission is required. Please grant it in System Settings > Privacy & Security > Screen Recording." "Screen recording permission is required. " +
"Please grant it in System Settings > Privacy & Security > Screen Recording."
case .accessibilityPermissionDenied: case .accessibilityPermissionDenied:
"Accessibility permission is required for some operations. Please grant it in System Settings > Privacy & Security > Accessibility." "Accessibility permission is required for some operations. " +
"Please grant it in System Settings > Privacy & Security > Accessibility."
case .invalidDisplayID: case .invalidDisplayID:
"Invalid display ID provided." "Invalid display ID provided."
case .captureCreationFailed: case .captureCreationFailed:

View file

@ -3,6 +3,6 @@
// To use this file for development, copy it to Version.swift: // To use this file for development, copy it to Version.swift:
// cp Version.swift.development Version.swift // cp Version.swift.development Version.swift
struct Version { enum Version {
static let current = "dev" static let current = "dev"
} }

View file

@ -1,10 +1,9 @@
import AppKit
@testable import peekaboo @testable import peekaboo
import Testing import Testing
import AppKit
@Suite("ApplicationFinder Tests", .tags(.applicationFinder, .unit)) @Suite("ApplicationFinder Tests", .tags(.applicationFinder, .unit))
struct ApplicationFinderTests { struct ApplicationFinderTests {
// MARK: - Test Data // MARK: - Test Data
private static let testIdentifiers = [ private static let testIdentifiers = [
@ -15,6 +14,7 @@ struct ApplicationFinderTests {
"", " ", "NonExistentApp12345", "invalid.bundle.id", "", " ", "NonExistentApp12345", "invalid.bundle.id",
String(repeating: "a", count: 1000) String(repeating: "a", count: 1000)
] ]
// MARK: - Find Application Tests // MARK: - Find Application Tests
@Test("Finding an app by exact name match", .tags(.fast)) @Test("Finding an app by exact name match", .tags(.fast))
@ -61,13 +61,15 @@ struct ApplicationFinderTests {
// MARK: - Parameterized Tests // MARK: - Parameterized Tests
@Test("Finding apps with various identifiers", @Test(
arguments: [ "Finding apps with various identifiers",
("Finder", "com.apple.finder"), arguments: [
("finder", "com.apple.finder"), ("Finder", "com.apple.finder"),
("FINDER", "com.apple.finder"), ("finder", "com.apple.finder"),
("com.apple.finder", "com.apple.finder") ("FINDER", "com.apple.finder"),
]) ("com.apple.finder", "com.apple.finder")
]
)
func findApplicationVariousIdentifiers(identifier: String, expectedBundleId: String) throws { func findApplicationVariousIdentifiers(identifier: String, expectedBundleId: String) throws {
let result = try ApplicationFinder.findApplication(identifier: identifier) let result = try ApplicationFinder.findApplication(identifier: identifier)
#expect(result.bundleIdentifier == expectedBundleId) #expect(result.bundleIdentifier == expectedBundleId)
@ -81,7 +83,7 @@ struct ApplicationFinderTests {
let apps = ApplicationFinder.getAllRunningApplications() let apps = ApplicationFinder.getAllRunningApplications()
// Should have at least some apps running // Should have at least some apps running
#expect(apps.count > 0) #expect(!apps.isEmpty)
// Should include Finder // Should include Finder
let hasFinder = apps.contains { $0.app_name == "Finder" } let hasFinder = apps.contains { $0.app_name == "Finder" }
@ -130,13 +132,15 @@ struct ApplicationFinderTests {
#expect(findResult.localizedName == "Finder") #expect(findResult.localizedName == "Finder")
} }
@Test("Bundle identifier parsing edge cases", @Test(
arguments: [ "Bundle identifier parsing edge cases",
"com.apple", arguments: [
"apple.finder", "com.apple",
"finder", "apple.finder",
"com.apple.finder.extra" "finder",
]) "com.apple.finder.extra"
]
)
func bundleIdentifierEdgeCases(partialBundleId: String) throws { func bundleIdentifierEdgeCases(partialBundleId: String) throws {
// Should either find Finder or throw appropriate error // Should either find Finder or throw appropriate error
do { do {
@ -156,8 +160,10 @@ struct ApplicationFinderTests {
#expect(result.bundleIdentifier == "com.apple.finder") #expect(result.bundleIdentifier == "com.apple.finder")
} }
@Test("Performance: Finding apps multiple times", @Test(
arguments: 1...10) "Performance: Finding apps multiple times",
arguments: 1...10
)
func findApplicationPerformance(iteration: Int) throws { func findApplicationPerformance(iteration: Int) throws {
// Test that finding an app completes quickly even when called multiple times // Test that finding an app completes quickly even when called multiple times
let result = try ApplicationFinder.findApplication(identifier: "Finder") let result = try ApplicationFinder.findApplication(identifier: "Finder")
@ -168,7 +174,7 @@ struct ApplicationFinderTests {
func stressTestManyApps() { func stressTestManyApps() {
// Get current app count for baseline // Get current app count for baseline
let apps = ApplicationFinder.getAllRunningApplications() let apps = ApplicationFinder.getAllRunningApplications()
#expect(apps.count > 0) #expect(!apps.isEmpty)
// Test search performance doesn't degrade with app list size // Test search performance doesn't degrade with app list size
let startTime = CFAbsoluteTimeGetCurrent() let startTime = CFAbsoluteTimeGetCurrent()
@ -183,12 +189,14 @@ struct ApplicationFinderTests {
// MARK: - Integration Tests // MARK: - Integration Tests
@Test("Find and verify running state of system apps", @Test(
arguments: [ "Find and verify running state of system apps",
("Finder", true), arguments: [
("Dock", true), ("Finder", true),
("SystemUIServer", true) ("Dock", true),
]) ("SystemUIServer", true)
]
)
func verifySystemAppsRunning(appName: String, shouldBeRunning: Bool) throws { func verifySystemAppsRunning(appName: String, shouldBeRunning: Bool) throws {
do { do {
let result = try ApplicationFinder.findApplication(identifier: appName) let result = try ApplicationFinder.findApplication(identifier: appName)
@ -227,7 +235,6 @@ struct ApplicationFinderTests {
@Suite("ApplicationFinder Edge Cases", .tags(.applicationFinder, .unit)) @Suite("ApplicationFinder Edge Cases", .tags(.applicationFinder, .unit))
struct ApplicationFinderEdgeCaseTests { struct ApplicationFinderEdgeCaseTests {
@Test("Empty identifier throws appropriate error", .tags(.fast)) @Test("Empty identifier throws appropriate error", .tags(.fast))
func emptyIdentifierError() { func emptyIdentifierError() {
#expect(throws: (any Error).self) { #expect(throws: (any Error).self) {
@ -250,8 +257,10 @@ struct ApplicationFinderEdgeCaseTests {
} }
} }
@Test("Unicode identifiers are handled correctly", @Test(
arguments: ["😀App", "App™", "Приложение", "アプリ"]) "Unicode identifiers are handled correctly",
arguments: ["😀App", "App™", "Приложение", "アプリ"]
)
func unicodeIdentifiers(identifier: String) { func unicodeIdentifiers(identifier: String) {
// Should not crash, either finds or throws appropriate error // Should not crash, either finds or throws appropriate error
do { do {
@ -291,10 +300,8 @@ struct ApplicationFinderEdgeCaseTests {
} }
var successCount = 0 var successCount = 0
for await success in group { for await success in group where success {
if success { successCount += 1
successCount += 1
}
} }
// All searches should succeed for Finder // All searches should succeed for Finder
@ -307,7 +314,7 @@ struct ApplicationFinderEdgeCaseTests {
// Test memory doesn't grow excessively with repeated calls // Test memory doesn't grow excessively with repeated calls
for _ in 1...5 { for _ in 1...5 {
let apps = ApplicationFinder.getAllRunningApplications() let apps = ApplicationFinder.getAllRunningApplications()
#expect(apps.count > 0) #expect(!apps.isEmpty)
} }
// If we get here without crashing, memory management is working // If we get here without crashing, memory management is working

View file

@ -1,11 +1,10 @@
import AppKit
import Foundation
@testable import peekaboo @testable import peekaboo
import Testing import Testing
import Foundation
import AppKit
@Suite("Image Capture Logic Tests", .tags(.imageCapture, .unit)) @Suite("Image Capture Logic Tests", .tags(.imageCapture, .unit))
struct ImageCaptureLogicTests { struct ImageCaptureLogicTests {
// MARK: - File Name Generation Tests // MARK: - File Name Generation Tests
@Test("File name generation for displays", .tags(.fast)) @Test("File name generation for displays", .tags(.fast))
@ -136,8 +135,10 @@ struct ImageCaptureLogicTests {
#expect(command.screenIndex == 1) #expect(command.screenIndex == 1)
} }
@Test("Screen index edge cases", @Test(
arguments: [-1, 0, 1, 5, 99, Int.max]) "Screen index edge cases",
arguments: [-1, 0, 1, 5, 99, Int.max]
)
func screenIndexEdgeCases(index: Int) throws { func screenIndexEdgeCases(index: Int) throws {
let command = try ImageCommand.parse([ let command = try ImageCommand.parse([
"--mode", "screen", "--mode", "screen",
@ -212,7 +213,7 @@ struct ImageCaptureLogicTests {
// We can't directly test the private method, but verify the errors exist // We can't directly test the private method, but verify the errors exist
// Verify the error exists (non-nil check not needed for value types) // Verify the error exists (non-nil check not needed for value types)
#expect(Bool(true)) #expect(Bool(true))
#expect(expectedCode.rawValue.count > 0) #expect(!expectedCode.rawValue.isEmpty)
} }
} }
@ -378,7 +379,6 @@ struct ImageCaptureLogicTests {
@Suite("Advanced Image Capture Logic", .tags(.imageCapture, .integration)) @Suite("Advanced Image Capture Logic", .tags(.imageCapture, .integration))
struct AdvancedImageCaptureLogicTests { struct AdvancedImageCaptureLogicTests {
@Test("Multi-mode capture scenarios", .tags(.fast)) @Test("Multi-mode capture scenarios", .tags(.fast))
func multiModeCaptureScenarios() throws { func multiModeCaptureScenarios() throws {
// Multi mode with app (should capture all windows) // Multi mode with app (should capture all windows)
@ -498,12 +498,12 @@ struct AdvancedImageCaptureLogicTests {
["--mode", "multi", "--app", String(repeating: "LongAppName", count: 100)], ["--mode", "multi", "--app", String(repeating: "LongAppName", count: 100)],
["--window-title", String(repeating: "VeryLongTitle", count: 200)], ["--window-title", String(repeating: "VeryLongTitle", count: 200)],
["--path", String(repeating: "/very/long/path", count: 50)], ["--path", String(repeating: "/very/long/path", count: 50)],
Array(repeating: ["--mode", "screen"], count: 100).flatMap { $0 } Array(repeating: ["--mode", "screen"], count: 100).flatMap(\.self)
] ]
for config in complexConfigs { for config in complexConfigs {
do { do {
let _ = try ImageCommand.parse(config) _ = try ImageCommand.parse(config)
#expect(Bool(true)) // Command parsed successfully #expect(Bool(true)) // Command parsed successfully
} catch { } catch {
// Some may fail due to argument parsing limits, which is expected // Some may fail due to argument parsing limits, which is expected

View file

@ -1,11 +1,10 @@
import ArgumentParser import ArgumentParser
import Foundation
@testable import peekaboo @testable import peekaboo
import Testing import Testing
import Foundation
@Suite("ImageCommand Tests", .tags(.imageCapture, .unit)) @Suite("ImageCommand Tests", .tags(.imageCapture, .unit))
struct ImageCommandTests { struct ImageCommandTests {
// MARK: - Test Data & Helpers // MARK: - Test Data & Helpers
private static let validFormats: [ImageFormat] = [.png, .jpg] private static let validFormats: [ImageFormat] = [.png, .jpg]
@ -13,7 +12,7 @@ struct ImageCommandTests {
private static let validCaptureFocus: [CaptureFocus] = [.background, .foreground] private static let validCaptureFocus: [CaptureFocus] = [.background, .foreground]
private static func createTestCommand(_ args: [String] = []) throws -> ImageCommand { private static func createTestCommand(_ args: [String] = []) throws -> ImageCommand {
return try ImageCommand.parse(args) try ImageCommand.parse(args)
} }
// MARK: - Command Parsing Tests // MARK: - Command Parsing Tests
@ -124,25 +123,29 @@ struct ImageCommandTests {
// MARK: - Parameterized Command Tests // MARK: - Parameterized Command Tests
@Test("Various command combinations", @Test(
arguments: [ "Various command combinations",
(args: ["--mode", "screen", "--format", "png"], mode: CaptureMode.screen, format: ImageFormat.png), arguments: [
(args: ["--mode", "window", "--format", "jpg"], mode: CaptureMode.window, format: ImageFormat.jpg), (args: ["--mode", "screen", "--format", "png"], mode: CaptureMode.screen, format: ImageFormat.png),
(args: ["--mode", "multi", "--json-output"], mode: CaptureMode.multi, format: ImageFormat.png) (args: ["--mode", "window", "--format", "jpg"], mode: CaptureMode.window, format: ImageFormat.jpg),
]) (args: ["--mode", "multi", "--json-output"], mode: CaptureMode.multi, format: ImageFormat.png)
]
)
func commandCombinations(args: [String], mode: CaptureMode, format: ImageFormat) throws { func commandCombinations(args: [String], mode: CaptureMode, format: ImageFormat) throws {
let command = try ImageCommand.parse(args) let command = try ImageCommand.parse(args)
#expect(command.mode == mode) #expect(command.mode == mode)
#expect(command.format == format) #expect(command.format == format)
} }
@Test("Invalid arguments throw errors", @Test(
arguments: [ "Invalid arguments throw errors",
["--mode", "invalid"], arguments: [
["--format", "bmp"], ["--mode", "invalid"],
["--capture-focus", "neither"], ["--format", "bmp"],
["--screen-index", "abc"] ["--capture-focus", "neither"],
]) ["--screen-index", "abc"]
]
)
func invalidArguments(args: [String]) { func invalidArguments(args: [String]) {
#expect(throws: (any Error).self) { #expect(throws: (any Error).self) {
_ = try ImageCommand.parse(args) _ = try ImageCommand.parse(args)
@ -185,7 +188,7 @@ struct ImageCommandTests {
encoder.keyEncodingStrategy = .convertToSnakeCase encoder.keyEncodingStrategy = .convertToSnakeCase
let data = try encoder.encode(captureData) let data = try encoder.encode(captureData)
#expect(data.count > 0) #expect(!data.isEmpty)
// Test decoding // Test decoding
let decoder = JSONDecoder() let decoder = JSONDecoder()
@ -251,15 +254,19 @@ struct ImageCommandTests {
#expect(command.jsonOutput == false) #expect(command.jsonOutput == false)
} }
@Test("Screen index boundary values", @Test(
arguments: [-1, 0, 1, 99, Int.max]) "Screen index boundary values",
arguments: [-1, 0, 1, 99, Int.max]
)
func screenIndexBoundaries(index: Int) throws { func screenIndexBoundaries(index: Int) throws {
let command = try ImageCommand.parse(["--screen-index", String(index)]) let command = try ImageCommand.parse(["--screen-index", String(index)])
#expect(command.screenIndex == index) #expect(command.screenIndex == index)
} }
@Test("Window index boundary values", @Test(
arguments: [-1, 0, 1, 10, Int.max]) "Window index boundary values",
arguments: [-1, 0, 1, 10, Int.max]
)
func windowIndexBoundaries(index: Int) throws { func windowIndexBoundaries(index: Int) throws {
let command = try ImageCommand.parse(["--window-index", String(index)]) let command = try ImageCommand.parse(["--window-index", String(index)])
#expect(command.windowIndex == index) #expect(command.windowIndex == index)
@ -283,7 +290,6 @@ struct ImageCommandTests {
@Suite("ImageCommand Advanced Tests", .tags(.imageCapture, .integration)) @Suite("ImageCommand Advanced Tests", .tags(.imageCapture, .integration))
struct ImageCommandAdvancedTests { struct ImageCommandAdvancedTests {
// MARK: - Complex Scenario Tests // MARK: - Complex Scenario Tests
@Test("Complex command with multiple options", .tags(.fast)) @Test("Complex command with multiple options", .tags(.fast))
@ -331,20 +337,22 @@ struct ImageCommandAdvancedTests {
#expect(config.abstract.contains("Capture")) #expect(config.abstract.contains("Capture"))
} }
@Test("Window specifier combinations", @Test(
arguments: [ "Window specifier combinations",
(app: "Safari", title: "Home", index: nil), arguments: [
(app: "Finder", title: nil, index: 0), (app: "Safari", title: "Home", index: nil),
(app: "Terminal", title: nil, index: nil) (app: "Finder", title: nil, index: 0),
]) (app: "Terminal", title: nil, index: nil)
]
)
func windowSpecifierCombinations(app: String, title: String?, index: Int?) throws { func windowSpecifierCombinations(app: String, title: String?, index: Int?) throws {
var args = ["--app", app] var args = ["--app", app]
if let title = title { if let title {
args.append(contentsOf: ["--window-title", title]) args.append(contentsOf: ["--window-title", title])
} }
if let index = index { if let index {
args.append(contentsOf: ["--window-index", String(index)]) args.append(contentsOf: ["--window-index", String(index)])
} }
@ -355,13 +363,15 @@ struct ImageCommandAdvancedTests {
#expect(command.windowIndex == index) #expect(command.windowIndex == index)
} }
@Test("Path expansion handling", @Test(
arguments: [ "Path expansion handling",
"~/Desktop/screenshot.png", arguments: [
"/tmp/test.png", "~/Desktop/screenshot.png",
"./relative/path.png", "/tmp/test.png",
"/path with spaces/image.png" "./relative/path.png",
]) "/path with spaces/image.png"
]
)
func pathExpansion(path: String) throws { func pathExpansion(path: String) throws {
let command = try ImageCommand.parse(["--path", path]) let command = try ImageCommand.parse(["--path", path])
#expect(command.path == path) #expect(command.path == path)
@ -405,12 +415,12 @@ struct ImageCommandAdvancedTests {
} }
@Test("MIME type assignment logic", .tags(.fast)) @Test("MIME type assignment logic", .tags(.fast))
func mimeTypeAssignment() { func mimeTypeAssignment() throws {
// Test MIME type logic for different formats // Test MIME type logic for different formats
let pngCommand = try! ImageCommand.parse(["--format", "png"]) let pngCommand = try ImageCommand.parse(["--format", "png"])
#expect(pngCommand.format == .png) #expect(pngCommand.format == .png)
let jpgCommand = try! ImageCommand.parse(["--format", "jpg"]) let jpgCommand = try ImageCommand.parse(["--format", "jpg"])
#expect(jpgCommand.format == .jpg) #expect(jpgCommand.format == .jpg)
// Verify MIME types would be assigned correctly // Verify MIME types would be assigned correctly
@ -439,16 +449,18 @@ struct ImageCommandAdvancedTests {
} }
} }
@Test("Command option combinations validation", @Test(
arguments: [ "Command option combinations validation",
(["--mode", "screen"], true), arguments: [
(["--mode", "window", "--app", "Finder"], true), (["--mode", "screen"], true),
(["--mode", "multi"], true), (["--mode", "window", "--app", "Finder"], true),
(["--app", "Safari"], true), (["--mode", "multi"], true),
(["--window-title", "Test"], true), (["--app", "Safari"], true),
(["--screen-index", "0"], true), (["--window-title", "Test"], true),
(["--window-index", "0"], true) (["--screen-index", "0"], true),
]) (["--window-index", "0"], true)
]
)
func commandOptionCombinations(args: [String], shouldParse: Bool) { func commandOptionCombinations(args: [String], shouldParse: Bool) {
do { do {
let command = try ImageCommand.parse(args) let command = try ImageCommand.parse(args)

View file

@ -1,10 +1,9 @@
import Foundation
@testable import peekaboo @testable import peekaboo
import Testing import Testing
import Foundation
@Suite("JSONOutput Tests", .tags(.jsonOutput, .unit)) @Suite("JSONOutput Tests", .tags(.jsonOutput, .unit))
struct JSONOutputTests { struct JSONOutputTests {
// MARK: - AnyCodable Tests // MARK: - AnyCodable Tests
@Test("AnyCodable encoding with various types", .tags(.fast)) @Test("AnyCodable encoding with various types", .tags(.fast))
@ -54,7 +53,8 @@ struct JSONOutputTests {
@Test("AnyCodable decoding", .tags(.fast)) @Test("AnyCodable decoding", .tags(.fast))
func anyCodableDecoding() throws { func anyCodableDecoding() throws {
// Test decoding from JSON // Test decoding from JSON
let jsonData = #"{"string": "test", "number": 42, "bool": true, "null": null}"#.data(using: .utf8)! let jsonString = #"{"string": "test", "number": 42, "bool": true, "null": null}"#
let jsonData = Data(jsonString.utf8)
let decoded = try JSONDecoder().decode([String: AnyCodable].self, from: jsonData) let decoded = try JSONDecoder().decode([String: AnyCodable].self, from: jsonData)
#expect(decoded["string"]?.value as? String == "test") #expect(decoded["string"]?.value as? String == "test")
@ -195,7 +195,7 @@ struct JSONOutputTests {
app_name: "App \(index)", app_name: "App \(index)",
bundle_id: "com.test.app\(index)", bundle_id: "com.test.app\(index)",
pid: Int32(1000 + index), pid: Int32(1000 + index),
is_active: index % 2 == 0, is_active: index.isMultiple(of: 2),
window_count: index % 10 window_count: index % 10
) )
largeAppList.append(appInfo) largeAppList.append(appInfo)
@ -208,27 +208,27 @@ struct JSONOutputTests {
let encoded = try JSONEncoder().encode(data) let encoded = try JSONEncoder().encode(data)
let encodingTime = CFAbsoluteTimeGetCurrent() - startTime let encodingTime = CFAbsoluteTimeGetCurrent() - startTime
#expect(encoded.count > 0) #expect(!encoded.isEmpty)
#expect(encodingTime < 1.0) // Should encode within 1 second #expect(encodingTime < 1.0) // Should encode within 1 second
} }
@Test("Thread safety of JSON operations", .tags(.concurrency)) @Test("Thread safety of JSON operations", .tags(.concurrency))
func threadSafetyJSONOperations() async { func threadSafetyJSONOperations() async {
await withTaskGroup(of: Bool.self) { group in await withTaskGroup(of: Bool.self) { group in
for i in 0..<10 { for index in 0..<10 {
group.addTask { group.addTask {
do { do {
let appInfo = ApplicationInfo( let appInfo = ApplicationInfo(
app_name: "App \(i)", app_name: "App \(index)",
bundle_id: "com.test.app\(i)", bundle_id: "com.test.app\(index)",
pid: Int32(1000 + i), pid: Int32(1000 + index),
is_active: true, is_active: true,
window_count: 1 window_count: 1
) )
// Test encoding through AnyCodable instead // Test encoding through AnyCodable instead
let anyCodable = AnyCodable(appInfo) let anyCodable = AnyCodable(appInfo)
let _ = try JSONEncoder().encode(anyCodable) _ = try JSONEncoder().encode(anyCodable)
return true return true
} catch { } catch {
return false return false
@ -237,10 +237,8 @@ struct JSONOutputTests {
} }
var successCount = 0 var successCount = 0
for await success in group { for await success in group where success {
if success { successCount += 1
successCount += 1
}
} }
#expect(successCount == 10) #expect(successCount == 10)
@ -261,7 +259,7 @@ struct JSONOutputTests {
do { do {
let encoded = try JSONEncoder().encode(data) let encoded = try JSONEncoder().encode(data)
#expect(encoded.count > 0) #expect(!encoded.isEmpty)
} catch { } catch {
Issue.record("JSON encoding should not fail: \(error)") Issue.record("JSON encoding should not fail: \(error)")
} }
@ -285,7 +283,7 @@ struct JSONOutputTests {
for errorCode in errorCodes { for errorCode in errorCodes {
#expect(!errorCode.rawValue.isEmpty) #expect(!errorCode.rawValue.isEmpty)
#expect(errorCode.rawValue.allSatisfy { $0.isASCII }) #expect(errorCode.rawValue.allSatisfy(\.isASCII))
} }
} }
} }
@ -294,7 +292,6 @@ struct JSONOutputTests {
@Suite("JSON Output Format Validation", .tags(.jsonOutput, .integration)) @Suite("JSON Output Format Validation", .tags(.jsonOutput, .integration))
struct JSONOutputFormatValidationTests { struct JSONOutputFormatValidationTests {
@Test("MCP protocol compliance", .tags(.integration)) @Test("MCP protocol compliance", .tags(.integration))
func mcpProtocolCompliance() throws { func mcpProtocolCompliance() throws {
// Test that JSON output follows MCP protocol format // Test that JSON output follows MCP protocol format
@ -357,7 +354,7 @@ struct JSONOutputFormatValidationTests {
window_id: UInt32(1000 + index), window_id: UInt32(1000 + index),
window_index: index, window_index: index,
bounds: WindowBounds(xCoordinate: index * 10, yCoordinate: index * 10, width: 800, height: 600), bounds: WindowBounds(xCoordinate: index * 10, yCoordinate: index * 10, width: 800, height: 600),
is_on_screen: index % 2 == 0 is_on_screen: index.isMultiple(of: 2)
) )
windows.append(window) windows.append(window)
} }
@ -375,11 +372,11 @@ struct JSONOutputFormatValidationTests {
let encoded = try JSONEncoder().encode(windowData) let encoded = try JSONEncoder().encode(windowData)
let duration = CFAbsoluteTimeGetCurrent() - startTime let duration = CFAbsoluteTimeGetCurrent() - startTime
#expect(encoded.count > 0) #expect(!encoded.isEmpty)
#expect(duration < 0.5) // Should complete within 500ms #expect(duration < 0.5) // Should complete within 500ms
// Verify the JSON is valid // Verify the JSON is valid
let _ = try JSONSerialization.jsonObject(with: encoded) _ = try JSONSerialization.jsonObject(with: encoded)
#expect(Bool(true)) // JSON was successfully created #expect(Bool(true)) // JSON was successfully created
} }
} }

View file

@ -1,7 +1,7 @@
import ArgumentParser import ArgumentParser
import Foundation
@testable import peekaboo @testable import peekaboo
import Testing import Testing
import Foundation
@Suite("ListCommand Tests", .tags(.unit)) @Suite("ListCommand Tests", .tags(.unit))
struct ListCommandTests { struct ListCommandTests {
@ -62,15 +62,17 @@ struct ListCommandTests {
// MARK: - Parameterized Command Tests // MARK: - Parameterized Command Tests
@Test("WindowsSubcommand detail parsing", @Test(
arguments: [ "WindowsSubcommand detail parsing",
"off_screen", arguments: [
"bounds", "off_screen",
"ids", "bounds",
"off_screen,bounds", "ids",
"bounds,ids", "off_screen,bounds",
"off_screen,bounds,ids" "bounds,ids",
]) "off_screen,bounds,ids"
]
)
func windowsDetailParsing(details: String) throws { func windowsDetailParsing(details: String) throws {
let command = try WindowsSubcommand.parse([ let command = try WindowsSubcommand.parse([
"--app", "Safari", "--app", "Safari",
@ -245,8 +247,10 @@ struct ListCommandTests {
// MARK: - Performance Tests // MARK: - Performance Tests
@Test("ApplicationListData encoding performance", @Test(
arguments: [10, 50, 100, 200]) "ApplicationListData encoding performance",
arguments: [10, 50, 100, 200]
)
func applicationListEncodingPerformance(appCount: Int) throws { func applicationListEncodingPerformance(appCount: Int) throws {
// Test performance of encoding many applications // Test performance of encoding many applications
let apps = (0..<appCount).map { index in let apps = (0..<appCount).map { index in
@ -265,7 +269,7 @@ struct ListCommandTests {
// Ensure encoding works correctly // Ensure encoding works correctly
let data = try encoder.encode(appData) let data = try encoder.encode(appData)
#expect(data.count > 0) #expect(!data.isEmpty)
} }
} }
@ -273,7 +277,6 @@ struct ListCommandTests {
@Suite("ListCommand Advanced Tests", .tags(.integration)) @Suite("ListCommand Advanced Tests", .tags(.integration))
struct ListCommandAdvancedTests { struct ListCommandAdvancedTests {
@Test("ServerStatusSubcommand parsing", .tags(.fast)) @Test("ServerStatusSubcommand parsing", .tags(.fast))
func serverStatusSubcommandParsing() throws { func serverStatusSubcommandParsing() throws {
let command = try ServerStatusSubcommand.parse([]) let command = try ServerStatusSubcommand.parse([])
@ -298,12 +301,14 @@ struct ListCommandAdvancedTests {
#expect(statusHelp.contains("status")) #expect(statusHelp.contains("status"))
} }
@Test("Complex window info structures", @Test(
arguments: [ "Complex window info structures",
(title: "Main Window", id: 1001, onScreen: true), arguments: [
(title: "Hidden Window", id: 2001, onScreen: false), (title: "Main Window", id: 1001, onScreen: true),
(title: "Minimized", id: 3001, onScreen: false) (title: "Hidden Window", id: 2001, onScreen: false),
]) (title: "Minimized", id: 3001, onScreen: false)
]
)
func complexWindowInfo(title: String, id: UInt32, onScreen: Bool) throws { func complexWindowInfo(title: String, id: UInt32, onScreen: Bool) throws {
let windowInfo = WindowInfo( let windowInfo = WindowInfo(
window_title: title, window_title: title,
@ -326,13 +331,15 @@ struct ListCommandAdvancedTests {
#expect(decoded.is_on_screen == onScreen) #expect(decoded.is_on_screen == onScreen)
} }
@Test("Application state combinations", @Test(
arguments: [ "Application state combinations",
(active: true, windowCount: 5), arguments: [
(active: false, windowCount: 0), (active: true, windowCount: 5),
(active: true, windowCount: 0), (active: false, windowCount: 0),
(active: false, windowCount: 10) (active: true, windowCount: 0),
]) (active: false, windowCount: 10)
]
)
func applicationStates(active: Bool, windowCount: Int) { func applicationStates(active: Bool, windowCount: Int) {
let appInfo = ApplicationInfo( let appInfo = ApplicationInfo(
app_name: "TestApp", app_name: "TestApp",

View file

@ -1,10 +1,9 @@
import Foundation
@testable import peekaboo @testable import peekaboo
import Testing import Testing
import Foundation
@Suite("Logger Tests", .tags(.logger, .unit), .serialized) @Suite("Logger Tests", .tags(.logger, .unit), .serialized)
struct LoggerTests { struct LoggerTests {
// MARK: - Basic Functionality Tests // MARK: - Basic Functionality Tests
@Test("Logger singleton instance", .tags(.fast)) @Test("Logger singleton instance", .tags(.fast))
@ -121,11 +120,11 @@ struct LoggerTests {
await withTaskGroup(of: Void.self) { group in await withTaskGroup(of: Void.self) { group in
// Create multiple concurrent logging tasks // Create multiple concurrent logging tasks
for i in 0..<10 { for index in 0..<10 {
group.addTask { group.addTask {
logger.debug("Concurrent message \(i)") logger.debug("Concurrent message \(index)")
logger.info("Concurrent info \(i)") logger.info("Concurrent info \(index)")
logger.error("Concurrent error \(i)") logger.error("Concurrent error \(index)")
} }
} }
} }
@ -141,10 +140,8 @@ struct LoggerTests {
// Verify no corruption by checking for our messages // Verify no corruption by checking for our messages
let recentLogs = finalLogs.suffix(30) let recentLogs = finalLogs.suffix(30)
var foundMessages = 0 var foundMessages = 0
for i in 0..<10 { for index in 0..<10 where recentLogs.contains(where: { $0.contains("Concurrent message \(index)") }) {
if recentLogs.contains(where: { $0.contains("Concurrent message \(i)") }) { foundMessages += 1
foundMessages += 1
}
} }
// Should find most or all messages (allowing for some timing issues) // Should find most or all messages (allowing for some timing issues)
@ -161,15 +158,15 @@ struct LoggerTests {
await withTaskGroup(of: Void.self) { group in await withTaskGroup(of: Void.self) { group in
// Task 1: Rapid mode switching // Task 1: Rapid mode switching
group.addTask { group.addTask {
for i in 0..<50 { for index in 0..<50 {
logger.setJsonOutputMode(i % 2 == 0) logger.setJsonOutputMode(index.isMultiple(of: 2))
} }
} }
// Task 2: Continuous logging during mode switches // Task 2: Continuous logging during mode switches
group.addTask { group.addTask {
for i in 0..<100 { for index in 0..<100 {
logger.debug("Mode switch test \(i)") logger.debug("Mode switch test \(index)")
} }
} }
@ -177,7 +174,7 @@ struct LoggerTests {
group.addTask { group.addTask {
for _ in 0..<10 { for _ in 0..<10 {
let logs = logger.getDebugLogs() let logs = logger.getDebugLogs()
#expect(logs.count >= 0) // Should not crash // Logs count is always non-negative
} }
} }
} }
@ -202,10 +199,10 @@ struct LoggerTests {
let initialCount = logger.getDebugLogs().count let initialCount = logger.getDebugLogs().count
// Generate many log messages // Generate many log messages
for i in 1...100 { for index in 1...100 {
logger.debug("Memory test message \(i)") logger.debug("Memory test message \(index)")
logger.info("Memory test info \(i)") logger.info("Memory test info \(index)")
logger.error("Memory test error \(i)") logger.error("Memory test error \(index)")
} }
// Wait for logging // Wait for logging
@ -264,8 +261,8 @@ struct LoggerTests {
let messageCount = 1000 let messageCount = 1000
let startTime = CFAbsoluteTimeGetCurrent() let startTime = CFAbsoluteTimeGetCurrent()
for i in 1...messageCount { for index in 1...messageCount {
logger.debug("Performance test message \(i)") logger.debug("Performance test message \(index)")
} }
let duration = CFAbsoluteTimeGetCurrent() - startTime let duration = CFAbsoluteTimeGetCurrent() - startTime
@ -284,8 +281,8 @@ struct LoggerTests {
let logger = Logger.shared let logger = Logger.shared
// Add many messages first // Add many messages first
for i in 1...100 { for index in 1...100 {
logger.debug("Retrieval test \(i)") logger.debug("Retrieval test \(index)")
} }
// Measure retrieval performance // Measure retrieval performance
@ -293,7 +290,7 @@ struct LoggerTests {
for _ in 1...10 { for _ in 1...10 {
let logs = logger.getDebugLogs() let logs = logger.getDebugLogs()
#expect(logs.count > 0) #expect(!logs.isEmpty)
} }
let duration = CFAbsoluteTimeGetCurrent() - startTime let duration = CFAbsoluteTimeGetCurrent() - startTime
@ -467,8 +464,8 @@ struct LoggerTests {
try? await Task.sleep(nanoseconds: 10_000_000) // 10ms try? await Task.sleep(nanoseconds: 10_000_000) // 10ms
// Test consistent JSON mode logging // Test consistent JSON mode logging
for i in 1...10 { for index in 1...10 {
logger.debug("State test \(i)") logger.debug("State test \(index)")
} }
// Wait for logging // Wait for logging

View file

@ -1,6 +1,6 @@
import CoreGraphics
@testable import peekaboo @testable import peekaboo
import Testing import Testing
import CoreGraphics
@Suite("Models Tests", .tags(.models, .unit)) @Suite("Models Tests", .tags(.models, .unit))
struct ModelsTests { struct ModelsTests {
@ -266,13 +266,19 @@ struct ModelsTests {
@Test("CaptureError descriptions are user-friendly", .tags(.fast)) @Test("CaptureError descriptions are user-friendly", .tags(.fast))
func captureErrorDescriptions() { func captureErrorDescriptions() {
#expect(CaptureError.noDisplaysAvailable.errorDescription == "No displays available for capture.") #expect(CaptureError.noDisplaysAvailable.errorDescription == "No displays available for capture.")
#expect(CaptureError.screenRecordingPermissionDenied.errorDescription!.contains("Screen recording permission is required")) #expect(CaptureError.screenRecordingPermissionDenied.errorDescription!
.contains("Screen recording permission is required")
)
#expect(CaptureError.invalidDisplayID.errorDescription == "Invalid display ID provided.") #expect(CaptureError.invalidDisplayID.errorDescription == "Invalid display ID provided.")
#expect(CaptureError.captureCreationFailed.errorDescription == "Failed to create the screen capture.") #expect(CaptureError.captureCreationFailed.errorDescription == "Failed to create the screen capture.")
#expect(CaptureError.windowNotFound.errorDescription == "The specified window could not be found.") #expect(CaptureError.windowNotFound.errorDescription == "The specified window could not be found.")
#expect(CaptureError.windowCaptureFailed.errorDescription == "Failed to capture the specified window.") #expect(CaptureError.windowCaptureFailed.errorDescription == "Failed to capture the specified window.")
#expect(CaptureError.fileWriteError("/tmp/test.png").errorDescription == "Failed to write capture file to path: /tmp/test.png.") #expect(CaptureError.fileWriteError("/tmp/test.png")
#expect(CaptureError.appNotFound("Safari").errorDescription == "Application with identifier 'Safari' not found or is not running.") .errorDescription == "Failed to write capture file to path: /tmp/test.png."
)
#expect(CaptureError.appNotFound("Safari")
.errorDescription == "Application with identifier 'Safari' not found or is not running."
)
#expect(CaptureError.invalidWindowIndex(5).errorDescription == "Invalid window index: 5.") #expect(CaptureError.invalidWindowIndex(5).errorDescription == "Invalid window index: 5.")
} }
@ -346,17 +352,18 @@ struct ModelsTests {
@Suite("Model Edge Cases", .tags(.models, .unit)) @Suite("Model Edge Cases", .tags(.models, .unit))
struct ModelEdgeCaseTests { struct ModelEdgeCaseTests {
@Test(
@Test("WindowBounds with edge values", "WindowBounds with edge values",
arguments: [ arguments: [
(x: 0, y: 0, width: 0, height: 0), (x: 0, y: 0, width: 0, height: 0),
(x: -100, y: -100, width: 100, height: 100), (x: -100, y: -100, width: 100, height: 100),
(x: Int.max, y: Int.max, width: 1, height: 1) (x: Int.max, y: Int.max, width: 1, height: 1)
]) ]
func windowBoundsEdgeCases(x: Int, y: Int, width: Int, height: Int) { )
let bounds = WindowBounds(xCoordinate: x, yCoordinate: y, width: width, height: height) func windowBoundsEdgeCases(x xCoordinate: Int, y yCoordinate: Int, width: Int, height: Int) {
#expect(bounds.xCoordinate == x) let bounds = WindowBounds(xCoordinate: xCoordinate, yCoordinate: yCoordinate, width: width, height: height)
#expect(bounds.yCoordinate == y) #expect(bounds.xCoordinate == xCoordinate)
#expect(bounds.yCoordinate == yCoordinate)
#expect(bounds.width == width) #expect(bounds.width == width)
#expect(bounds.height == height) #expect(bounds.height == height)
} }
@ -377,15 +384,17 @@ struct ModelEdgeCaseTests {
#expect(appInfo.window_count == Int.max) #expect(appInfo.window_count == Int.max)
} }
@Test("SavedFile path validation", @Test(
arguments: [ "SavedFile path validation",
"/tmp/test.png", arguments: [
"/Users/test/Desktop/screenshot.jpg", "/tmp/test.png",
"~/Documents/capture.png", "/Users/test/Desktop/screenshot.jpg",
"./relative/path/image.png", "~/Documents/capture.png",
"/path with spaces/image.png", "./relative/path/image.png",
"/path/with/特殊文字.png" "/path with spaces/image.png",
]) "/path/with/特殊文字.png"
]
)
func savedFilePathValidation(path: String) { func savedFilePathValidation(path: String) {
let savedFile = SavedFile( let savedFile = SavedFile(
path: path, path: path,
@ -400,8 +409,10 @@ struct ModelEdgeCaseTests {
#expect(!savedFile.path.isEmpty) #expect(!savedFile.path.isEmpty)
} }
@Test("MIME type validation", @Test(
arguments: ["image/png", "image/jpeg", "image/jpg"]) "MIME type validation",
arguments: ["image/png", "image/jpeg", "image/jpg"]
)
func mimeTypeValidation(mimeType: String) { func mimeTypeValidation(mimeType: String) {
let savedFile = SavedFile( let savedFile = SavedFile(
path: "/tmp/test", path: "/tmp/test",

View file

@ -1,6 +1,6 @@
import AppKit
@testable import peekaboo @testable import peekaboo
import Testing import Testing
import AppKit
@Suite("PermissionsChecker Tests", .tags(.permissions, .unit)) @Suite("PermissionsChecker Tests", .tags(.permissions, .unit))
struct PermissionsCheckerTests { struct PermissionsCheckerTests {
@ -131,7 +131,6 @@ struct PermissionsCheckerTests {
@Suite("Permission Edge Cases", .tags(.permissions, .unit)) @Suite("Permission Edge Cases", .tags(.permissions, .unit))
struct PermissionEdgeCaseTests { struct PermissionEdgeCaseTests {
@Test("Permission checks are thread-safe", .tags(.integration)) @Test("Permission checks are thread-safe", .tags(.integration))
func threadSafePermissionChecks() async { func threadSafePermissionChecks() async {
// Test concurrent permission checks // Test concurrent permission checks

View file

@ -10,13 +10,13 @@ struct WindowManagerTests {
func getWindowsForFinderApp() throws { func getWindowsForFinderApp() throws {
// Get Finder's PID // Get Finder's PID
let apps = NSWorkspace.shared.runningApplications let apps = NSWorkspace.shared.runningApplications
let finder = try #require(apps.first(where: { $0.bundleIdentifier == "com.apple.finder" })) let finder = try #require(apps.first { $0.bundleIdentifier == "com.apple.finder" })
// Test getting windows for Finder // Test getting windows for Finder
let windows = try WindowManager.getWindowsForApp(pid: finder.processIdentifier) let windows = try WindowManager.getWindowsForApp(pid: finder.processIdentifier)
// Finder usually has at least one window // Finder usually has at least one window
#expect(windows.count >= 0) // Windows count is always non-negative
// If there are windows, verify they're sorted by index // If there are windows, verify they're sorted by index
if windows.count > 1 { if windows.count > 1 {
@ -32,14 +32,14 @@ struct WindowManagerTests {
let windows = try WindowManager.getWindowsForApp(pid: 99999) let windows = try WindowManager.getWindowsForApp(pid: 99999)
// Should return empty array, not throw // Should return empty array, not throw
#expect(windows.count == 0) #expect(windows.isEmpty)
} }
@Test("Off-screen window filtering works correctly", .tags(.integration)) @Test("Off-screen window filtering works correctly", .tags(.integration))
func getWindowsWithOffScreenOption() throws { func getWindowsWithOffScreenOption() throws {
// Get Finder's PID for testing // Get Finder's PID for testing
let apps = NSWorkspace.shared.runningApplications let apps = NSWorkspace.shared.runningApplications
let finder = try #require(apps.first(where: { $0.bundleIdentifier == "com.apple.finder" })) let finder = try #require(apps.first { $0.bundleIdentifier == "com.apple.finder" })
// Test with includeOffScreen = true // Test with includeOffScreen = true
let allWindows = try WindowManager.getWindowsForApp(pid: finder.processIdentifier, includeOffScreen: true) let allWindows = try WindowManager.getWindowsForApp(pid: finder.processIdentifier, includeOffScreen: true)
@ -103,13 +103,15 @@ struct WindowManagerTests {
// MARK: - Parameterized Tests // MARK: - Parameterized Tests
@Test("Window retrieval with various options", @Test(
arguments: [ "Window retrieval with various options",
(includeOffScreen: true, includeBounds: true, includeIDs: true), arguments: [
(includeOffScreen: false, includeBounds: true, includeIDs: true), (includeOffScreen: true, includeBounds: true, includeIDs: true),
(includeOffScreen: true, includeBounds: false, includeIDs: true), (includeOffScreen: false, includeBounds: true, includeIDs: true),
(includeOffScreen: true, includeBounds: true, includeIDs: false) (includeOffScreen: true, includeBounds: false, includeIDs: true),
]) (includeOffScreen: true, includeBounds: true, includeIDs: false)
]
)
func windowRetrievalOptions(includeOffScreen: Bool, includeBounds: Bool, includeIDs: Bool) throws { func windowRetrievalOptions(includeOffScreen: Bool, includeBounds: Bool, includeIDs: Bool) throws {
let apps = NSWorkspace.shared.runningApplications.filter { $0.activationPolicy == .regular } let apps = NSWorkspace.shared.runningApplications.filter { $0.activationPolicy == .regular }
@ -144,15 +146,17 @@ struct WindowManagerTests {
// MARK: - Performance Tests // MARK: - Performance Tests
@Test("Window retrieval performance", @Test(
arguments: 1...5) "Window retrieval performance",
arguments: 1...5
)
func getWindowsPerformance(iteration: Int) throws { func getWindowsPerformance(iteration: Int) throws {
// Test performance of getting windows // Test performance of getting windows
let apps = NSWorkspace.shared.runningApplications let apps = NSWorkspace.shared.runningApplications
let finder = try #require(apps.first(where: { $0.bundleIdentifier == "com.apple.finder" })) let finder = try #require(apps.first { $0.bundleIdentifier == "com.apple.finder" })
let windows = try WindowManager.getWindowsForApp(pid: finder.processIdentifier) let windows = try WindowManager.getWindowsForApp(pid: finder.processIdentifier)
#expect(windows.count >= 0) // Windows count is always non-negative
} }
// MARK: - Error Handling Tests // MARK: - Error Handling Tests
@ -176,7 +180,6 @@ struct WindowManagerTests {
@Suite("WindowManager Advanced Tests", .tags(.windowManager, .integration)) @Suite("WindowManager Advanced Tests", .tags(.windowManager, .integration))
struct WindowManagerAdvancedTests { struct WindowManagerAdvancedTests {
@Test("Multiple apps window retrieval", .tags(.integration)) @Test("Multiple apps window retrieval", .tags(.integration))
func multipleAppsWindows() throws { func multipleAppsWindows() throws {
let apps = NSWorkspace.shared.runningApplications.filter { $0.activationPolicy == .regular } let apps = NSWorkspace.shared.runningApplications.filter { $0.activationPolicy == .regular }
@ -186,7 +189,7 @@ struct WindowManagerAdvancedTests {
let windows = try WindowManager.getWindowsForApp(pid: app.processIdentifier) let windows = try WindowManager.getWindowsForApp(pid: app.processIdentifier)
// Each app should successfully return a window list (even if empty) // Each app should successfully return a window list (even if empty)
#expect(windows.count >= 0) // Windows count is always non-negative
// Verify window indices are sequential // Verify window indices are sequential
for (index, window) in windows.enumerated() { for (index, window) in windows.enumerated() {
@ -214,8 +217,10 @@ struct WindowManagerAdvancedTests {
} }
} }
@Test("System apps window detection", @Test(
arguments: ["com.apple.finder", "com.apple.dock", "com.apple.systemuiserver"]) "System apps window detection",
arguments: ["com.apple.finder", "com.apple.dock", "com.apple.systemuiserver"]
)
func systemAppsWindows(bundleId: String) throws { func systemAppsWindows(bundleId: String) throws {
let apps = NSWorkspace.shared.runningApplications let apps = NSWorkspace.shared.runningApplications
@ -226,7 +231,7 @@ struct WindowManagerAdvancedTests {
let windows = try WindowManager.getWindowsForApp(pid: app.processIdentifier) let windows = try WindowManager.getWindowsForApp(pid: app.processIdentifier)
// System apps might have 0 or more windows // System apps might have 0 or more windows
#expect(windows.count >= 0) // Windows count is always non-negative
// If windows exist, they should have valid properties // If windows exist, they should have valid properties
for window in windows { for window in windows {
@ -245,7 +250,7 @@ struct WindowManagerAdvancedTests {
for window in windows { for window in windows {
// Title should be valid UTF-8 // Title should be valid UTF-8
#expect(window.title.utf8.count > 0) #expect(!window.title.utf8.isEmpty)
// Should handle common special characters // Should handle common special characters
let specialChars = ["", "", "©", "", ""] let specialChars = ["", "", "©", "", ""]
@ -287,9 +292,9 @@ struct WindowManagerAdvancedTests {
#expect(results.count == 5) #expect(results.count == 5)
for result in results { for result in results {
switch result { switch result {
case .success(let windows): case let .success(windows):
#expect(windows.count >= 0) // Windows count is always non-negative
case .failure(let error): case let .failure(error):
Issue.record("Concurrent query failed: \(error)") Issue.record("Concurrent query failed: \(error)")
} }
} }