add tests for path handling

This commit is contained in:
Peter Steinberger 2025-06-08 03:48:49 +01:00
parent 0c4706393e
commit 8145455fc4
4 changed files with 336 additions and 7 deletions

View file

@ -203,7 +203,7 @@ struct ImageCaptureLogicTests {
(.accessibilityPermissionDenied, .PERMISSION_ERROR_ACCESSIBILITY),
(.appNotFound("test"), .APP_NOT_FOUND),
(.windowNotFound, .WINDOW_NOT_FOUND),
(.fileWriteError("test"), .FILE_IO_ERROR),
(.fileWriteError("test", nil), .FILE_IO_ERROR),
(.invalidArgument("test"), .INVALID_ARGUMENT),
(.unknownError("test"), .UNKNOWN_ERROR)
]

View file

@ -286,6 +286,336 @@ struct ImageCommandTests {
}
}
// MARK: - Path Handling Tests
@Suite("ImageCommand Path Handling Tests", .tags(.imageCapture, .unit))
struct ImageCommandPathHandlingTests {
// MARK: - Helper Methods
private func createTestImageCommand(path: String?, screenIndex: Int? = nil) -> ImageCommand {
var command = ImageCommand()
command.path = path
command.screenIndex = screenIndex
command.format = .png
return command
}
// MARK: - Path Detection Tests
@Test("File vs directory path detection", .tags(.fast))
func pathDetection() {
// Test file-like paths (have extension, no trailing slash)
let filePaths = [
"/tmp/screenshot.png",
"/home/user/image.jpg",
"/path/with spaces/file.png",
"./relative/file.png",
"simple.png"
]
// Test directory-like paths (no extension or trailing slash)
let directoryPaths = [
"/tmp/",
"/home/user/screenshots",
"/path/with spaces/",
"simple-dir"
]
// File paths should be detected correctly
for filePath in filePaths {
let isLikelyFile = filePath.contains(".") && !filePath.hasSuffix("/")
#expect(isLikelyFile == true, "Path '\(filePath)' should be detected as file")
}
// Directory paths should be detected correctly
for dirPath in directoryPaths {
let isLikelyFile = dirPath.contains(".") && !dirPath.hasSuffix("/")
#expect(isLikelyFile == false, "Path '\(dirPath)' should be detected as directory")
}
}
@Test("Single screen file path handling", .tags(.fast))
func singleScreenFilePath() {
let command = createTestImageCommand(path: "/tmp/my-screenshot.png", screenIndex: 0)
// For single screen, should use exact path
let fileName = "screen_1_20250608_120000.png"
let result = command.determineOutputPath(basePath: "/tmp/my-screenshot.png", fileName: fileName)
#expect(result == "/tmp/my-screenshot.png")
}
@Test("Multiple screens file path handling", .tags(.fast))
func multipleScreensFilePath() {
let command = createTestImageCommand(path: "/tmp/screenshot.png", screenIndex: nil)
// For multiple screens, should append screen info
let fileName = "screen_1_20250608_120000.png"
let result = command.determineOutputPath(basePath: "/tmp/screenshot.png", fileName: fileName)
#expect(result == "/tmp/screenshot_1_20250608_120000.png")
}
@Test("Directory path handling", .tags(.fast))
func directoryPathHandling() {
let command = createTestImageCommand(path: "/tmp/screenshots", screenIndex: nil)
let fileName = "screen_1_20250608_120000.png"
let result = command.determineOutputPath(basePath: "/tmp/screenshots", fileName: fileName)
#expect(result == "/tmp/screenshots/screen_1_20250608_120000.png")
}
@Test("Directory with trailing slash handling", .tags(.fast))
func directoryWithTrailingSlashHandling() {
let command = createTestImageCommand(path: "/tmp/screenshots/", screenIndex: nil)
let fileName = "screen_1_20250608_120000.png"
let result = command.determineOutputPath(basePath: "/tmp/screenshots/", fileName: fileName)
#expect(result == "/tmp/screenshots//screen_1_20250608_120000.png")
}
@Test(
"Various file extensions",
arguments: [
"/tmp/image.png",
"/tmp/photo.jpg",
"/tmp/picture.jpeg",
"/tmp/screen.PNG",
"/tmp/capture.JPG"
]
)
func variousFileExtensions(path: String) {
let command = createTestImageCommand(path: path, screenIndex: nil)
let fileName = "screen_1_20250608_120000.png"
let result = command.determineOutputPath(basePath: path, fileName: fileName)
// Should modify the filename for multiple screens, keeping original extension
let pathExtension = (path as NSString).pathExtension
let pathWithoutExtension = (path as NSString).deletingPathExtension
let expected = "\(pathWithoutExtension)_1_20250608_120000.\(pathExtension)"
#expect(result == expected)
}
@Test(
"Edge case paths",
arguments: [
("", false), // Empty path
("...", true), // File-like with dots
("/", false), // Root directory
("/tmp/.hidden", true), // Hidden file
("/tmp/.hidden/", false), // Hidden directory
("file.tar.gz", true), // Multiple extensions
]
)
func edgeCasePaths(path: String, expectedAsFile: Bool) {
let isLikelyFile = path.contains(".") && !path.hasSuffix("/")
#expect(isLikelyFile == expectedAsFile, "Path '\(path)' detection failed")
}
@Test("Filename generation with screen suffix extraction", .tags(.fast))
func filenameSuffixExtraction() {
let command = createTestImageCommand(path: "/tmp/shot.png", screenIndex: nil)
// Test various filename patterns
let testCases = [
(fileName: "screen_1_20250608_120000.png", expected: "/tmp/shot_1_20250608_120000.png"),
(fileName: "screen_2_20250608_120001.png", expected: "/tmp/shot_2_20250608_120001.png"),
(fileName: "screen_10_20250608_120002.png", expected: "/tmp/shot_10_20250608_120002.png")
]
for testCase in testCases {
let result = command.determineOutputPath(basePath: "/tmp/shot.png", fileName: testCase.fileName)
#expect(result == testCase.expected, "Failed for fileName: \(testCase.fileName)")
}
}
@Test("Path with special characters", .tags(.fast))
func pathWithSpecialCharacters() {
let specialPaths = [
"/tmp/测试 screenshot.png",
"/tmp/スクリーン capture.png",
"/tmp/screen-shot_v2.png",
"/tmp/my file (1).png"
]
for path in specialPaths {
let command = createTestImageCommand(path: path, screenIndex: 0)
let fileName = "screen_1_20250608_120000.png"
let result = command.determineOutputPath(basePath: path, fileName: fileName)
// For single screen, should use exact path
#expect(result == path, "Failed for special path: \(path)")
}
}
@Test("Nested directory path creation logic", .tags(.fast))
func nestedDirectoryPathCreation() {
let nestedPaths = [
"/tmp/very/deep/nested/path/file.png",
"/home/user/Documents/Screenshots/test.jpg",
"./relative/deep/path/image.png"
]
for path in nestedPaths {
let command = createTestImageCommand(path: path, screenIndex: 0)
let fileName = "screen_1_20250608_120000.png"
let result = command.determineOutputPath(basePath: path, fileName: fileName)
#expect(result == path, "Should return exact path for nested file: \(path)")
// Test parent directory extraction
let parentDir = (path as NSString).deletingLastPathComponent
#expect(!parentDir.isEmpty, "Parent directory should be extractable from: \(path)")
}
}
@Test("Default path behavior (nil path)", .tags(.fast))
func defaultPathBehavior() {
let command = createTestImageCommand(path: nil)
let fileName = "screen_1_20250608_120000.png"
let result = command.getOutputPath(fileName)
#expect(result == "/tmp/\(fileName)")
}
@Test("getOutputPath method delegation", .tags(.fast))
func getOutputPathDelegation() {
// Test that getOutputPath properly delegates to determineOutputPath
let command = createTestImageCommand(path: "/tmp/test.png")
let fileName = "screen_1_20250608_120000.png"
let result = command.getOutputPath(fileName)
// Should call determineOutputPath and return its result
#expect(result.contains("/tmp/test"))
#expect(result.hasSuffix(".png"))
}
}
// MARK: - Error Handling Tests
@Suite("ImageCommand Error Handling Tests", .tags(.imageCapture, .unit))
struct ImageCommandErrorHandlingTests {
@Test("Improved file write error messages", .tags(.fast))
func improvedFileWriteErrorMessages() {
// Test enhanced error messages with different underlying errors
// Test with permission error
let permissionError = NSError(domain: NSCocoaErrorDomain, code: NSFileWriteNoPermissionError, userInfo: [
NSLocalizedDescriptionKey: "Permission denied"
])
let fileErrorWithPermission = CaptureError.fileWriteError("/tmp/test.png", permissionError)
let permissionMessage = fileErrorWithPermission.errorDescription ?? ""
#expect(permissionMessage.contains("Failed to write capture file to path: /tmp/test.png."))
#expect(permissionMessage.contains("Permission denied - check that the directory is writable"))
// Test with no such file error
let noFileError = NSError(domain: NSCocoaErrorDomain, code: NSFileNoSuchFileError, userInfo: [
NSLocalizedDescriptionKey: "No such file or directory"
])
let fileErrorWithNoFile = CaptureError.fileWriteError("/tmp/nonexistent/test.png", noFileError)
let noFileMessage = fileErrorWithNoFile.errorDescription ?? ""
#expect(noFileMessage.contains("Failed to write capture file to path: /tmp/nonexistent/test.png."))
#expect(noFileMessage.contains("Directory does not exist - ensure the parent directory exists"))
// Test with disk space error
let spaceError = NSError(domain: NSCocoaErrorDomain, code: NSFileWriteOutOfSpaceError, userInfo: [
NSLocalizedDescriptionKey: "No space left on device"
])
let fileErrorWithSpace = CaptureError.fileWriteError("/tmp/test.png", spaceError)
let spaceMessage = fileErrorWithSpace.errorDescription ?? ""
#expect(spaceMessage.contains("Failed to write capture file to path: /tmp/test.png."))
#expect(spaceMessage.contains("Insufficient disk space available"))
// Test with generic error
let genericError = NSError(domain: "TestDomain", code: 999, userInfo: [
NSLocalizedDescriptionKey: "Some generic error"
])
let fileErrorWithGeneric = CaptureError.fileWriteError("/tmp/test.png", genericError)
let genericMessage = fileErrorWithGeneric.errorDescription ?? ""
#expect(genericMessage.contains("Failed to write capture file to path: /tmp/test.png."))
#expect(genericMessage.contains("Some generic error"))
// Test with no underlying error
let fileErrorWithoutUnderlying = CaptureError.fileWriteError("/tmp/test.png", nil)
let noUnderlyingMessage = fileErrorWithoutUnderlying.errorDescription ?? ""
#expect(noUnderlyingMessage.contains("Failed to write capture file to path: /tmp/test.png."))
#expect(noUnderlyingMessage.contains("This may be due to insufficient permissions, missing directory, or disk space issues"))
}
@Test("Error message formatting consistency", .tags(.fast))
func errorMessageFormattingConsistency() {
// Test that all error messages end with proper punctuation and format
let testPath = "/tmp/test/path/file.png"
let testError = NSError(domain: "Test", code: 1, userInfo: [NSLocalizedDescriptionKey: "Test error"])
let fileError = CaptureError.fileWriteError(testPath, testError)
let message = fileError.errorDescription ?? ""
// Should contain the path
#expect(message.contains(testPath))
// Should be properly formatted
#expect(message.starts(with: "Failed to write capture file to path:"))
// Should have additional context
#expect(message.count > "Failed to write capture file to path: \(testPath).".count)
}
@Test("Error exit codes consistency", .tags(.fast))
func errorExitCodesConsistency() {
// Test that file write errors maintain proper exit codes
let fileError1 = CaptureError.fileWriteError("/tmp/test1.png", nil)
let fileError2 = CaptureError.fileWriteError("/tmp/test2.png", NSError(domain: "Test", code: 1))
#expect(fileError1.exitCode == 17)
#expect(fileError2.exitCode == 17)
#expect(fileError1.exitCode == fileError2.exitCode)
}
@Test("Directory creation error handling", .tags(.fast))
func directoryCreationErrorHandling() {
// Test that directory creation failures are handled gracefully
// This test validates the logic without actually creating directories
var command = ImageCommand()
command.path = "/tmp/test-path-creation/file.png"
command.screenIndex = 0
let fileName = "screen_1_20250608_120000.png"
let result = command.determineOutputPath(basePath: "/tmp/test-path-creation/file.png", fileName: fileName)
// Should return the intended path even if directory creation might fail
#expect(result == "/tmp/test-path-creation/file.png")
}
@Test("Path validation edge cases", .tags(.fast))
func pathValidationEdgeCases() throws {
let command = try ImageCommand.parse([])
// Test empty path components
let emptyResult = command.determineOutputPath(basePath: "", fileName: "test.png")
#expect(emptyResult == "/test.png")
// Test root path
let rootResult = command.determineOutputPath(basePath: "/", fileName: "test.png")
#expect(rootResult == "//test.png")
// Test current directory
let currentResult = command.determineOutputPath(basePath: ".", fileName: "test.png")
#expect(currentResult == "./test.png")
}
}
// MARK: - Extended Image Command Tests
@Suite("ImageCommand Advanced Tests", .tags(.imageCapture, .integration))

View file

@ -273,9 +273,8 @@ struct ModelsTests {
#expect(CaptureError.captureCreationFailed.errorDescription == "Failed to create the screen capture.")
#expect(CaptureError.windowNotFound.errorDescription == "The specified window could not be found.")
#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."
)
let fileError = CaptureError.fileWriteError("/tmp/test.png", nil)
#expect(fileError.errorDescription?.starts(with: "Failed to write capture file to path: /tmp/test.png.") == true)
#expect(CaptureError.appNotFound("Safari")
.errorDescription == "Application with identifier 'Safari' not found or is not running."
)
@ -292,7 +291,7 @@ struct ModelsTests {
(.captureCreationFailed, 14),
(.windowNotFound, 15),
(.windowCaptureFailed, 16),
(.fileWriteError("test"), 17),
(.fileWriteError("test", nil), 17),
(.appNotFound("test"), 18),
(.invalidWindowIndex(0), 19),
(.invalidArgument("test"), 20),

View file

@ -287,7 +287,7 @@ struct ScreenshotValidationTests {
private func saveImage(_ image: NSImage, to path: String, format: ImageFormat) throws {
guard let tiffData = image.tiffRepresentation,
let bitmap = NSBitmapImageRep(data: tiffData) else {
throw CaptureError.fileWriteError(path)
throw CaptureError.fileWriteError(path, nil)
}
let data: Data? = switch format {
@ -298,7 +298,7 @@ struct ScreenshotValidationTests {
}
guard let imageData = data else {
throw CaptureError.fileWriteError(path)
throw CaptureError.fileWriteError(path, nil)
}
try imageData.write(to: URL(fileURLWithPath: path))