import AppKit
@testable import peepit
import Testing
@Suite("PermissionsChecker Tests", .tags(.permissions, .unit))
struct PermissionsCheckerTests {
// MARK: - Screen Recording Permission Tests
@Test("Screen recording permission check returns boolean", .tags(.fast))
func checkScreenRecordingPermission() {
// Test screen recording permission check
let hasPermission = PermissionsChecker.checkScreenRecordingPermission()
// Just verify we got a valid boolean result (the API works)
// The actual value depends on system permissions
_ = hasPermission
}
@Test("Screen recording permission check is consistent", .tags(.fast))
func screenRecordingPermissionConsistency() {
// Test that multiple calls return consistent results
let firstCheck = PermissionsChecker.checkScreenRecordingPermission()
let secondCheck = PermissionsChecker.checkScreenRecordingPermission()
#expect(firstCheck == secondCheck)
}
@Test("Screen recording permission check performance", arguments: 1...5)
func screenRecordingPermissionPerformance(iteration: Int) {
// Permission checks should be fast
_ = PermissionsChecker.checkScreenRecordingPermission()
// Performance is measured by the test framework's execution time
}
// MARK: - Accessibility Permission Tests
@Test("Accessibility permission check returns boolean", .tags(.fast))
func checkAccessibilityPermission() {
// Test accessibility permission check
let hasPermission = PermissionsChecker.checkAccessibilityPermission()
// Just verify we got a valid boolean result (the API works)
// The actual value depends on system permissions
_ = hasPermission
}
@Test("Accessibility permission matches AXIsProcessTrusted", .tags(.fast))
func accessibilityPermissionWithTrustedCheck() {
// Test the AXIsProcessTrusted check
let options = ["AXTrustedCheckOptionPrompt": false]
let isTrusted = AXIsProcessTrustedWithOptions(options as CFDictionary)
let hasPermission = PermissionsChecker.checkAccessibilityPermission()
// These should match
#expect(isTrusted == hasPermission)
}
// MARK: - Combined Permission Tests
@Test("Both permissions can be checked independently", .tags(.fast))
func bothPermissions() {
// Test both permission checks
let screenRecording = PermissionsChecker.checkScreenRecordingPermission()
let accessibility = PermissionsChecker.checkAccessibilityPermission()
// Both should return valid boolean values
#expect(screenRecording == true || screenRecording == false)
#expect(accessibility == true || accessibility == false)
}
// MARK: - Require Permission Tests
@Test("Require screen recording permission throws when denied", .tags(.fast))
func requireScreenRecordingPermission() {
let hasPermission = PermissionsChecker.checkScreenRecordingPermission()
if hasPermission {
// Should not throw when permission is granted
#expect(throws: Never.self) {
try PermissionsChecker.requireScreenRecordingPermission()
}
} else {
// Should throw specific error when permission is denied
#expect(throws: (any Error).self) {
try PermissionsChecker.requireScreenRecordingPermission()
}
}
}
@Test("Require accessibility permission throws when denied", .tags(.fast))
func requireAccessibilityPermission() {
let hasPermission = PermissionsChecker.checkAccessibilityPermission()
if hasPermission {
// Should not throw when permission is granted
#expect(throws: Never.self) {
try PermissionsChecker.requireAccessibilityPermission()
}
} else {
// Should throw specific error when permission is denied
#expect(throws: (any Error).self) {
try PermissionsChecker.requireAccessibilityPermission()
}
}
}
// MARK: - Error Message Tests
@Test("Permission errors have descriptive messages", .tags(.fast))
func permissionErrorMessages() {
let screenError = CaptureError.screenRecordingPermissionDenied
let accessError = CaptureError.accessibilityPermissionDenied
// CaptureError conforms to LocalizedError, so it has errorDescription
#expect(screenError.errorDescription != nil)
#expect(accessError.errorDescription != nil)
#expect(screenError.errorDescription!.contains("Screen recording permission"))
#expect(accessError.errorDescription!.contains("Accessibility permission"))
}
@Test("Permission errors have correct exit codes", .tags(.fast))
func permissionErrorExitCodes() {
let screenError = CaptureError.screenRecordingPermissionDenied
let accessError = CaptureError.accessibilityPermissionDenied
#expect(screenError.exitCode == 11)
#expect(accessError.exitCode == 12)
}
}
// MARK: - Extended Permission Tests
@Suite("Permission Edge Cases", .tags(.permissions, .unit))
struct PermissionEdgeCaseTests {
@Test("Permission checks are thread-safe", .tags(.integration))
func threadSafePermissionChecks() async {
// Test concurrent permission checks
await withTaskGroup(of: Bool.self) { group in
for _ in 0..<10 {
group.addTask {
PermissionsChecker.checkScreenRecordingPermission()
}
group.addTask {
PermissionsChecker.checkAccessibilityPermission()
}
}
var results: [Bool] = []
for await result in group {
results.append(result)
}
// All results should be valid booleans
#expect(results.count == 20)
for result in results {
#expect(result == true || result == false)
}
}
}
@Test("ScreenCaptureKit availability check", .tags(.fast))
func screenCaptureKitAvailable() {
// Verify that we can at least access ScreenCaptureKit APIs
// This is a basic smoke test to ensure the framework is available
let isAvailable = NSClassFromString("SCShareableContent") != nil
#expect(isAvailable == true)
}
@Test("Permission state changes are detected", .tags(.integration))
func permissionStateChanges() {
// This test verifies that permission checks reflect current state
// Note: This test cannot actually change permissions, but verifies
// that repeated checks could detect changes if they occurred
let initialScreen = PermissionsChecker.checkScreenRecordingPermission()
let initialAccess = PermissionsChecker.checkAccessibilityPermission()
// Sleep briefly to allow for potential state changes
Thread.sleep(forTimeInterval: 0.1)
let finalScreen = PermissionsChecker.checkScreenRecordingPermission()
let finalAccess = PermissionsChecker.checkAccessibilityPermission()
// In normal operation, these should be the same
// but the important thing is they reflect current state
#expect(initialScreen == finalScreen)
#expect(initialAccess == finalAccess)
}
}