From 37a9e7e2e1e517701394197e526209e4725a9922 Mon Sep 17 00:00:00 2001 From: Prasanna Gautam Date: Fri, 26 Dec 2025 03:55:39 -0800 Subject: [PATCH] Add comprehensive error handling and test infrastructure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add specific exit codes (0-7, 99) for different failure modes - Improve error messages to distinguish between: - No clipboard image vs conversion failure - Permission denied vs invalid path vs disk errors - Fix memory leak in parseArguments (NSString never released) - Add shell-based integration test suite with 32 tests - Add `make test` target Exit codes: 0: Success 1: Usage error 2: No clipboard image 3: Conversion error 4: Permission denied 5: Invalid path 6: Disk error 7: Stdout error 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- Makefile | 13 +++- README.md | 19 ++++- pngpaste.h | 21 +++++- pngpaste.m | 141 ++++++++++++++++++++++++++++------- tests/fixtures/sample.png | Bin 0 -> 334 bytes tests/run_tests.sh | 88 ++++++++++++++++++++++ tests/test_error_messages.sh | 48 ++++++++++++ tests/test_exit_codes.sh | 63 ++++++++++++++++ tests/test_helpers.sh | 119 +++++++++++++++++++++++++++++ tests/test_output_formats.sh | 114 ++++++++++++++++++++++++++++ tests/test_stdout.sh | 71 ++++++++++++++++++ 11 files changed, 664 insertions(+), 33 deletions(-) create mode 100644 tests/fixtures/sample.png create mode 100755 tests/run_tests.sh create mode 100755 tests/test_error_messages.sh create mode 100755 tests/test_exit_codes.sh create mode 100755 tests/test_helpers.sh create mode 100755 tests/test_output_formats.sh create mode 100755 tests/test_stdout.sh diff --git a/Makefile b/Makefile index 44cfbda..88bfe4a 100644 --- a/Makefile +++ b/Makefile @@ -1,13 +1,18 @@ +.PHONY: all install test clean all: $(CC) -Wall -g -O3 -ObjC \ -framework Foundation -framework AppKit \ -o pngpaste \ pngpaste.m + install: all cp pngpaste /usr/local/bin/ + +test: all + ./tests/run_tests.sh + clean: - find . \( -name '*~' -or -name '#*#' -or -name '*.o' \ - -or -name 'pngpaste' -or -name 'pngpaste.dSYM' \) \ - -exec rm -rfv {} \; - rm -rfv *.dSYM/ pngpaste; + rm -rfv *.dSYM/ pngpaste + find . \( -name '*~' -or -name '#*#' -or -name '*.o' \) \ + -exec rm -fv {} \; diff --git a/README.md b/README.md index 1298ba2..58979ac 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,21 @@ falling back to PNG. It's unclear if EXIF data in JPEG sources are preserved. There's an issue with pasting into JPEG format from a GIF source. -### Error Handling +### Exit Codes -Minimal :'( +| Code | Description | +|------|-------------| +| 0 | Success | +| 1 | Invalid arguments or usage error | +| 2 | No image data found on clipboard | +| 3 | Could not convert image format | +| 4 | Permission denied writing output file | +| 5 | Invalid output path | +| 6 | Disk full or I/O error | +| 7 | Error writing to stdout | + +### Testing + +Run the test suite: + + $ make test diff --git a/pngpaste.h b/pngpaste.h index 588d48d..51e5da5 100644 --- a/pngpaste.h +++ b/pngpaste.h @@ -11,6 +11,16 @@ #define PDF_SCALE_FACTOR 1.5 #define STDOUT_FILENAME "-" +// Exit codes +#define EXIT_ERR_USAGE 1 // Invalid arguments or usage error +#define EXIT_ERR_NO_CLIPBOARD 2 // No image data on clipboard +#define EXIT_ERR_CONVERT 3 // Could not convert image format +#define EXIT_ERR_WRITE_PERM 4 // Permission denied writing file +#define EXIT_ERR_WRITE_PATH 5 // Invalid output path +#define EXIT_ERR_WRITE_DISK 6 // Disk full or I/O error +#define EXIT_ERR_STDOUT 7 // Error writing to stdout +#define EXIT_ERR_INTERNAL 99 // Unexpected internal error + typedef enum imageType { ImageTypeNone = 0, @@ -28,16 +38,25 @@ typedef struct parameters BOOL malformed; } Parameters; +typedef struct pasteboardResult +{ + NSData *imageData; + int errorCode; + const char *errorMessage; +} PasteboardResult; + void usage (); void fatal (const char *msg); +void fatalWithCode (int exitCode, const char *msg); void version (); +int getWriteErrorExitCode (NSError *error); ImageType extractImageType (NSImage *image); NSData *renderImageData (NSImage *image, NSBitmapImageFileType bitmapImageFileType); NSData *renderFromBitmap (NSImage *image, NSBitmapImageFileType bitmapImageFileType); NSData *renderFromPDF (NSImage *image, NSBitmapImageFileType bitmapImageFileType); NSBitmapImageFileType getBitmapImageFileTypeFromFilename (NSString *filename); -NSData *getPasteboardImageData (NSBitmapImageFileType bitmapImageFileType); +PasteboardResult getPasteboardImageData (NSBitmapImageFileType bitmapImageFileType); Parameters parseArguments (int argc, char* const argv[]); diff --git a/pngpaste.m b/pngpaste.m index 4651c89..0712033 100644 --- a/pngpaste.m +++ b/pngpaste.m @@ -24,6 +24,44 @@ } } +void +fatalWithCode (int exitCode, const char *msg) +{ + if (msg != NULL) { + fprintf(stderr, "%s: %s (exit code %d)\n", APP_NAME, msg, exitCode); + } +} + +int +getWriteErrorExitCode (NSError *error) +{ + if (error == nil) { + return EXIT_ERR_WRITE_DISK; + } + + NSInteger code = [error code]; + + // Check for permission errors + if (code == NSFileWriteNoPermissionError || + code == NSFileReadNoPermissionError) { + return EXIT_ERR_WRITE_PERM; + } + + // Check for path errors + if (code == NSFileNoSuchFileError || + code == NSFileWriteInvalidFileNameError) { + return EXIT_ERR_WRITE_PATH; + } + + // Check for disk space errors + if (code == NSFileWriteOutOfSpaceError) { + return EXIT_ERR_WRITE_DISK; + } + + // Default to disk error for other write failures + return EXIT_ERR_WRITE_DISK; +} + void version () { @@ -130,21 +168,44 @@ } /* - * Returns NSData from Pasteboard Image if available; otherwise nil + * Returns PasteboardResult with image data or error information */ -NSData * +PasteboardResult getPasteboardImageData (NSBitmapImageFileType bitmapImageFileType) { + PasteboardResult result; + result.imageData = nil; + result.errorCode = EXIT_SUCCESS; + result.errorMessage = NULL; + NSPasteboard *pasteBoard = [NSPasteboard generalPasteboard]; NSImage *image = [[NSImage alloc] initWithPasteboard:pasteBoard]; - NSData *imageData = nil; - if (image != nil) { - imageData = renderImageData(image, bitmapImageFileType); + if (image == nil) { + result.errorCode = EXIT_ERR_NO_CLIPBOARD; + result.errorMessage = "No image data found on the clipboard"; + return result; } + ImageType imageType = extractImageType(image); + if (imageType == ImageTypeNone) { + result.errorCode = EXIT_ERR_CONVERT; + result.errorMessage = "Clipboard contains unsupported image type"; + [image release]; + return result; + } + + NSData *imageData = renderImageData(image, bitmapImageFileType); [image release]; - return imageData; + + if (imageData == nil) { + result.errorCode = EXIT_ERR_CONVERT; + result.errorMessage = "Could not convert clipboard image to requested format"; + return result; + } + + result.imageData = imageData; + return result; } Parameters @@ -200,7 +261,7 @@ Parameters params = parseArguments(argc, argv); if (params.malformed) { usage(); - return EXIT_FAILURE; + return EXIT_ERR_USAGE; } else if (params.wantsUsage) { usage(); return EXIT_SUCCESS; @@ -211,32 +272,60 @@ NSBitmapImageFileType bitmapImageFileType = getBitmapImageFileTypeFromFilename(params.outputFile); - NSData *imageData = getPasteboardImageData(bitmapImageFileType); + PasteboardResult result = getPasteboardImageData(bitmapImageFileType); int exitCode; - if (imageData != nil) { - if (params.wantsStdout) { - NSFileHandle *stdout = - (NSFileHandle *)[NSFileHandle fileHandleWithStandardOutput]; - [stdout writeData:imageData]; + if (result.errorCode != EXIT_SUCCESS) { + fatal(result.errorMessage); + exitCode = result.errorCode; + } else if (params.wantsStdout) { + NSFileHandle *stdoutHandle = + (NSFileHandle *)[NSFileHandle fileHandleWithStandardOutput]; + @try { + [stdoutHandle writeData:result.imageData]; + exitCode = EXIT_SUCCESS; + } @catch (NSException *exception) { + fatal("Error writing to stdout"); + exitCode = EXIT_ERR_STDOUT; + } + } else if (params.wantsBase64) { + NSFileHandle *stdoutHandle = + (NSFileHandle *)[NSFileHandle fileHandleWithStandardOutput]; + NSData *base64Data = [result.imageData base64EncodedDataWithOptions:0]; + @try { + [stdoutHandle writeData:base64Data]; exitCode = EXIT_SUCCESS; - } else if (params.wantsBase64) { - NSFileHandle *stdout = - (NSFileHandle *)[NSFileHandle fileHandleWithStandardOutput]; - NSData *base64Data = [imageData base64EncodedDataWithOptions:0]; - [stdout writeData:base64Data]; + } @catch (NSException *exception) { + fatal("Error writing to stdout"); + exitCode = EXIT_ERR_STDOUT; + } + } else { + NSError *writeError = nil; + BOOL success = [result.imageData writeToFile:params.outputFile + options:NSDataWritingAtomic + error:&writeError]; + if (success) { exitCode = EXIT_SUCCESS; } else { - if ([imageData writeToFile:params.outputFile atomically:YES]) { - exitCode = EXIT_SUCCESS; - } else { - fatal("Could not write to file!"); - exitCode = EXIT_FAILURE; + exitCode = getWriteErrorExitCode(writeError); + switch (exitCode) { + case EXIT_ERR_WRITE_PERM: + fatal("Permission denied writing to file"); + break; + case EXIT_ERR_WRITE_PATH: + fatal("Invalid output path"); + break; + case EXIT_ERR_WRITE_DISK: + default: + fatal("Could not write to file"); + break; } } - } else { - fatal("No image data found on the clipboard, or could not convert!"); - exitCode = EXIT_FAILURE; + } + + // Cleanup + if (params.outputFile != nil) { + [params.outputFile release]; } return exitCode; diff --git a/tests/fixtures/sample.png b/tests/fixtures/sample.png new file mode 100644 index 0000000000000000000000000000000000000000..e6f8ea44da0c4caeb32658a6226981e796178322 GIT binary patch literal 334 zcmeAS@N?(olHy`uVBq!ia0vp^DImsrKgQu&X%Q~loCIEb(W;Fl+ literal 0 HcmV?d00001 diff --git a/tests/run_tests.sh b/tests/run_tests.sh new file mode 100755 index 0000000..cd28461 --- /dev/null +++ b/tests/run_tests.sh @@ -0,0 +1,88 @@ +#!/bin/bash + +# Main test runner for pngpaste + +set -e + +TESTS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_DIR="$(dirname "$TESTS_DIR")" + +echo "========================================" +echo "pngpaste Test Suite" +echo "========================================" +echo "" + +# Build the project first +echo "Building pngpaste..." +make -C "$PROJECT_DIR" all + +if [ ! -f "$PROJECT_DIR/pngpaste" ]; then + echo "ERROR: Build failed, pngpaste binary not found" + exit 1 +fi + +echo "Build successful!" +echo "" + +# Show version +"$PROJECT_DIR/pngpaste" -v +echo "" + +# Check for test fixtures +if [ ! -f "$TESTS_DIR/fixtures/sample.png" ]; then + echo "WARNING: Test fixture sample.png not found" + echo "Creating test fixture..." + python3 -c " +import struct +import zlib + +def create_png(width, height, color): + def make_chunk(chunk_type, data): + chunk = chunk_type + data + return struct.pack('>I', len(data)) + chunk + struct.pack('>I', zlib.crc32(chunk) & 0xffffffff) + + header = b'\x89PNG\r\n\x1a\n' + ihdr = make_chunk(b'IHDR', struct.pack('>IIBBBBB', width, height, 8, 2, 0, 0, 0)) + raw_data = b''.join(b'\x00' + bytes(color) * width for _ in range(height)) + idat = make_chunk(b'IDAT', zlib.compress(raw_data)) + iend = make_chunk(b'IEND', b'') + return header + ihdr + idat + iend + +with open('$TESTS_DIR/fixtures/sample.png', 'wb') as f: + f.write(create_png(100, 100, [255, 0, 0])) +" + echo "Test fixture created" +fi + +echo "Running tests..." +echo "" + +TOTAL_FAILED=0 +TOTAL_TESTS=0 + +for test_script in "$TESTS_DIR"/test_*.sh; do + if [ -f "$test_script" ]; then + echo "" + chmod +x "$test_script" + if bash "$test_script"; then + : # Test passed + else + TOTAL_FAILED=$((TOTAL_FAILED + 1)) + fi + TOTAL_TESTS=$((TOTAL_TESTS + 1)) + fi +done + +echo "" +echo "========================================" +echo "Test Suite Summary" +echo "========================================" +echo "Test files run: $TOTAL_TESTS" + +if [ $TOTAL_FAILED -eq 0 ]; then + echo "Result: ALL PASSED" + exit 0 +else + echo "Result: $TOTAL_FAILED test file(s) had failures" + exit 1 +fi diff --git a/tests/test_error_messages.sh b/tests/test_error_messages.sh new file mode 100755 index 0000000..9f98a16 --- /dev/null +++ b/tests/test_error_messages.sh @@ -0,0 +1,48 @@ +#!/bin/bash + +# Test error messages for pngpaste + +source "$(dirname "$0")/test_helpers.sh" + +echo "=== Error Message Tests ===" + +# Test: Usage message on no arguments +stderr_output=$("$PNGPASTE" 2>&1) +assert_stderr_contains "Usage:" "$stderr_output" "No arguments shows usage" + +# Test: Version output +stderr_output=$("$PNGPASTE" -v 2>&1) +assert_stderr_contains "pngpaste" "$stderr_output" "Version shows app name" + +# Tests that require clipboard access +if clipboard_available; then + # Test: No clipboard image message + clear_clipboard + stderr_output=$("$PNGPASTE" /tmp/test.png 2>&1) + assert_stderr_contains "No image data" "$stderr_output" \ + "Empty clipboard shows appropriate message" + + # Test: Permission denied message + if copy_image_to_clipboard "$TESTS_DIR/fixtures/sample.png"; then + stderr_output=$("$PNGPASTE" /System/test.png 2>&1) + assert_stderr_contains "Permission denied" "$stderr_output" \ + "Permission denied shows appropriate message" + else + skip_test "Permission denied message" "Could not copy image to clipboard" + fi + + # Test: Invalid path message + if copy_image_to_clipboard "$TESTS_DIR/fixtures/sample.png"; then + stderr_output=$("$PNGPASTE" /nonexistent/dir/test.png 2>&1) + assert_stderr_contains "Invalid output path" "$stderr_output" \ + "Invalid path shows appropriate message" + else + skip_test "Invalid path message" "Could not copy image to clipboard" + fi +else + skip_test "Empty clipboard message" "Clipboard not available" + skip_test "Permission denied message" "Clipboard not available" + skip_test "Invalid path message" "Clipboard not available" +fi + +print_summary diff --git a/tests/test_exit_codes.sh b/tests/test_exit_codes.sh new file mode 100755 index 0000000..7e224a1 --- /dev/null +++ b/tests/test_exit_codes.sh @@ -0,0 +1,63 @@ +#!/bin/bash + +# Test exit codes for pngpaste + +source "$(dirname "$0")/test_helpers.sh" + +echo "=== Exit Code Tests ===" + +# Test: No arguments +"$PNGPASTE" 2>/dev/null +assert_exit_code 1 $? "No arguments returns EXIT_ERR_USAGE (1)" + +# Test: Help flag +"$PNGPASTE" -h 2>/dev/null +assert_exit_code 0 $? "Help flag returns EXIT_SUCCESS (0)" + +# Test: Version flag +"$PNGPASTE" -v 2>/dev/null +assert_exit_code 0 $? "Version flag returns EXIT_SUCCESS (0)" + +# Test: Unknown flag (getopt returns ? which maps to usage) +"$PNGPASTE" -z 2>/dev/null +assert_exit_code 0 $? "Unknown flag shows help (getopt returns ?)" + +# Tests that require clipboard access +if clipboard_available; then + # Test: No clipboard image + clear_clipboard + "$PNGPASTE" /tmp/pngpaste_test_output.png 2>/dev/null + assert_exit_code 2 $? "Empty clipboard returns EXIT_ERR_NO_CLIPBOARD (2)" + + # Test: Valid clipboard image + if copy_image_to_clipboard "$TESTS_DIR/fixtures/sample.png"; then + "$PNGPASTE" /tmp/pngpaste_test_output.png 2>/dev/null + assert_exit_code 0 $? "Valid clipboard image returns EXIT_SUCCESS (0)" + rm -f /tmp/pngpaste_test_output.png + else + skip_test "Valid clipboard image" "Could not copy image to clipboard" + fi + + # Test: Permission denied (write to root-owned directory) + if copy_image_to_clipboard "$TESTS_DIR/fixtures/sample.png"; then + "$PNGPASTE" /System/pngpaste_test.png 2>/dev/null + assert_exit_code 4 $? "Permission denied returns EXIT_ERR_WRITE_PERM (4)" + else + skip_test "Permission denied" "Could not copy image to clipboard" + fi + + # Test: Invalid path + if copy_image_to_clipboard "$TESTS_DIR/fixtures/sample.png"; then + "$PNGPASTE" /nonexistent/directory/test.png 2>/dev/null + assert_exit_code 5 $? "Invalid path returns EXIT_ERR_WRITE_PATH (5)" + else + skip_test "Invalid path" "Could not copy image to clipboard" + fi +else + skip_test "Empty clipboard" "Clipboard not available" + skip_test "Valid clipboard image" "Clipboard not available" + skip_test "Permission denied" "Clipboard not available" + skip_test "Invalid path" "Clipboard not available" +fi + +print_summary diff --git a/tests/test_helpers.sh b/tests/test_helpers.sh new file mode 100755 index 0000000..39de142 --- /dev/null +++ b/tests/test_helpers.sh @@ -0,0 +1,119 @@ +#!/bin/bash + +# Test framework utilities for pngpaste + +TESTS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_DIR="$(dirname "$TESTS_DIR")" +PNGPASTE="$PROJECT_DIR/pngpaste" + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[0;33m' +NC='\033[0m' # No Color + +TESTS_RUN=0 +TESTS_PASSED=0 +TESTS_FAILED=0 +TESTS_SKIPPED=0 + +# Copy image to clipboard using osascript +copy_image_to_clipboard() { + local image_path="$1" + osascript -e "set the clipboard to (read (POSIX file \"$image_path\") as TIFF picture)" 2>/dev/null + return $? +} + +# Clear clipboard +clear_clipboard() { + osascript -e "set the clipboard to \"\"" 2>/dev/null +} + +# Check if clipboard operations are available +clipboard_available() { + # Try a simple clipboard operation + osascript -e "get the clipboard" &>/dev/null + return $? +} + +# Assert exit code matches expected +assert_exit_code() { + local expected=$1 + local actual=$2 + local test_name=$3 + + TESTS_RUN=$((TESTS_RUN + 1)) + if [ "$actual" -eq "$expected" ]; then + echo -e "${GREEN}PASS${NC}: $test_name (exit code $actual)" + TESTS_PASSED=$((TESTS_PASSED + 1)) + return 0 + else + echo -e "${RED}FAIL${NC}: $test_name (expected $expected, got $actual)" + TESTS_FAILED=$((TESTS_FAILED + 1)) + return 1 + fi +} + +# Assert file exists +assert_file_exists() { + local file_path=$1 + local test_name=$2 + + TESTS_RUN=$((TESTS_RUN + 1)) + if [ -f "$file_path" ]; then + echo -e "${GREEN}PASS${NC}: $test_name" + TESTS_PASSED=$((TESTS_PASSED + 1)) + return 0 + else + echo -e "${RED}FAIL${NC}: $test_name (file not found: $file_path)" + TESTS_FAILED=$((TESTS_FAILED + 1)) + return 1 + fi +} + +# Assert stderr contains string +assert_stderr_contains() { + local expected="$1" + local actual="$2" + local test_name="$3" + + TESTS_RUN=$((TESTS_RUN + 1)) + if echo "$actual" | grep -q "$expected"; then + echo -e "${GREEN}PASS${NC}: $test_name" + TESTS_PASSED=$((TESTS_PASSED + 1)) + return 0 + else + echo -e "${RED}FAIL${NC}: $test_name (expected stderr to contain '$expected')" + echo -e " Actual: $actual" + TESTS_FAILED=$((TESTS_FAILED + 1)) + return 1 + fi +} + +# Skip test with message +skip_test() { + local test_name="$1" + local reason="$2" + + TESTS_RUN=$((TESTS_RUN + 1)) + TESTS_SKIPPED=$((TESTS_SKIPPED + 1)) + echo -e "${YELLOW}SKIP${NC}: $test_name ($reason)" +} + +# Print test summary +print_summary() { + echo "" + echo "================================" + echo "Tests run: $TESTS_RUN" + echo -e "Passed: ${GREEN}$TESTS_PASSED${NC}" + echo -e "Failed: ${RED}$TESTS_FAILED${NC}" + if [ "$TESTS_SKIPPED" -gt 0 ]; then + echo -e "Skipped: ${YELLOW}$TESTS_SKIPPED${NC}" + fi + echo "================================" + + if [ "$TESTS_FAILED" -gt 0 ]; then + return 1 + fi + return 0 +} diff --git a/tests/test_output_formats.sh b/tests/test_output_formats.sh new file mode 100755 index 0000000..9a4582e --- /dev/null +++ b/tests/test_output_formats.sh @@ -0,0 +1,114 @@ +#!/bin/bash + +# Test output format conversion for pngpaste + +source "$(dirname "$0")/test_helpers.sh" + +echo "=== Output Format Tests ===" + +# Check clipboard availability +if ! clipboard_available; then + echo "Clipboard not available, skipping format tests" + skip_test "PNG output" "Clipboard not available" + skip_test "JPEG output" "Clipboard not available" + skip_test "GIF output" "Clipboard not available" + skip_test "TIFF output" "Clipboard not available" + skip_test "Unknown extension defaults to PNG" "Clipboard not available" + print_summary + exit $? +fi + +# Create temp directory for test outputs +TEMP_DIR=$(mktemp -d) +trap "rm -rf $TEMP_DIR" EXIT + +# Copy test image to clipboard +if ! copy_image_to_clipboard "$TESTS_DIR/fixtures/sample.png"; then + echo "Could not copy test image to clipboard, skipping format tests" + print_summary + exit 1 +fi + +# Test PNG output +"$PNGPASTE" "$TEMP_DIR/output.png" 2>/dev/null +assert_exit_code 0 $? "PNG output succeeds" +assert_file_exists "$TEMP_DIR/output.png" "PNG file created" + +# Verify PNG format +if file "$TEMP_DIR/output.png" | grep -qi "png"; then + TESTS_RUN=$((TESTS_RUN + 1)) + TESTS_PASSED=$((TESTS_PASSED + 1)) + echo -e "${GREEN}PASS${NC}: PNG file has correct format" +else + TESTS_RUN=$((TESTS_RUN + 1)) + TESTS_FAILED=$((TESTS_FAILED + 1)) + echo -e "${RED}FAIL${NC}: PNG file has incorrect format" +fi + +# Test JPEG output +copy_image_to_clipboard "$TESTS_DIR/fixtures/sample.png" +"$PNGPASTE" "$TEMP_DIR/output.jpg" 2>/dev/null +assert_exit_code 0 $? "JPEG output succeeds" +assert_file_exists "$TEMP_DIR/output.jpg" "JPEG file created" + +# Verify JPEG format +if file "$TEMP_DIR/output.jpg" | grep -qi "jpeg"; then + TESTS_RUN=$((TESTS_RUN + 1)) + TESTS_PASSED=$((TESTS_PASSED + 1)) + echo -e "${GREEN}PASS${NC}: JPEG file has correct format" +else + TESTS_RUN=$((TESTS_RUN + 1)) + TESTS_FAILED=$((TESTS_FAILED + 1)) + echo -e "${RED}FAIL${NC}: JPEG file has incorrect format" +fi + +# Test GIF output +copy_image_to_clipboard "$TESTS_DIR/fixtures/sample.png" +"$PNGPASTE" "$TEMP_DIR/output.gif" 2>/dev/null +assert_exit_code 0 $? "GIF output succeeds" +assert_file_exists "$TEMP_DIR/output.gif" "GIF file created" + +# Verify GIF format +if file "$TEMP_DIR/output.gif" | grep -qi "gif"; then + TESTS_RUN=$((TESTS_RUN + 1)) + TESTS_PASSED=$((TESTS_PASSED + 1)) + echo -e "${GREEN}PASS${NC}: GIF file has correct format" +else + TESTS_RUN=$((TESTS_RUN + 1)) + TESTS_FAILED=$((TESTS_FAILED + 1)) + echo -e "${RED}FAIL${NC}: GIF file has incorrect format" +fi + +# Test TIFF output +copy_image_to_clipboard "$TESTS_DIR/fixtures/sample.png" +"$PNGPASTE" "$TEMP_DIR/output.tiff" 2>/dev/null +assert_exit_code 0 $? "TIFF output succeeds" +assert_file_exists "$TEMP_DIR/output.tiff" "TIFF file created" + +# Verify TIFF format +if file "$TEMP_DIR/output.tiff" | grep -qi "tiff"; then + TESTS_RUN=$((TESTS_RUN + 1)) + TESTS_PASSED=$((TESTS_PASSED + 1)) + echo -e "${GREEN}PASS${NC}: TIFF file has correct format" +else + TESTS_RUN=$((TESTS_RUN + 1)) + TESTS_FAILED=$((TESTS_FAILED + 1)) + echo -e "${RED}FAIL${NC}: TIFF file has incorrect format" +fi + +# Test unknown extension defaults to PNG +copy_image_to_clipboard "$TESTS_DIR/fixtures/sample.png" +"$PNGPASTE" "$TEMP_DIR/output.xyz" 2>/dev/null +assert_exit_code 0 $? "Unknown extension succeeds (defaults to PNG)" + +if file "$TEMP_DIR/output.xyz" | grep -qi "png"; then + TESTS_RUN=$((TESTS_RUN + 1)) + TESTS_PASSED=$((TESTS_PASSED + 1)) + echo -e "${GREEN}PASS${NC}: Unknown extension creates PNG format" +else + TESTS_RUN=$((TESTS_RUN + 1)) + TESTS_FAILED=$((TESTS_FAILED + 1)) + echo -e "${RED}FAIL${NC}: Unknown extension did not create PNG format" +fi + +print_summary diff --git a/tests/test_stdout.sh b/tests/test_stdout.sh new file mode 100755 index 0000000..045f982 --- /dev/null +++ b/tests/test_stdout.sh @@ -0,0 +1,71 @@ +#!/bin/bash + +# Test stdout and base64 output for pngpaste + +source "$(dirname "$0")/test_helpers.sh" + +echo "=== Stdout Output Tests ===" + +# Check clipboard availability +if ! clipboard_available; then + echo "Clipboard not available, skipping stdout tests" + skip_test "Stdout output succeeds" "Clipboard not available" + skip_test "Stdout contains PNG data" "Clipboard not available" + skip_test "Base64 output succeeds" "Clipboard not available" + skip_test "Base64 output is valid" "Clipboard not available" + print_summary + exit $? +fi + +# Copy test image to clipboard +if ! copy_image_to_clipboard "$TESTS_DIR/fixtures/sample.png"; then + echo "Could not copy test image to clipboard" + print_summary + exit 1 +fi + +# Test stdout binary output +output=$("$PNGPASTE" - 2>/dev/null) +assert_exit_code 0 $? "Stdout output succeeds" + +# Verify it's valid PNG data (check for PNG signature bytes) +# PNG signature: 89 50 4E 47 0D 0A 1A 0A +if echo "$output" | head -c 8 | od -A n -t x1 | tr -d ' \n' | grep -qi "89504e470d0a1a0a"; then + TESTS_RUN=$((TESTS_RUN + 1)) + TESTS_PASSED=$((TESTS_PASSED + 1)) + echo -e "${GREEN}PASS${NC}: Stdout contains PNG signature" +else + TESTS_RUN=$((TESTS_RUN + 1)) + TESTS_FAILED=$((TESTS_FAILED + 1)) + echo -e "${RED}FAIL${NC}: Stdout does not contain PNG signature" +fi + +# Test base64 output +copy_image_to_clipboard "$TESTS_DIR/fixtures/sample.png" +base64_output=$("$PNGPASTE" -b 2>/dev/null) +assert_exit_code 0 $? "Base64 output succeeds" + +# Verify it's valid base64 (should decode successfully) +if echo "$base64_output" | base64 -d > /dev/null 2>&1; then + TESTS_RUN=$((TESTS_RUN + 1)) + TESTS_PASSED=$((TESTS_PASSED + 1)) + echo -e "${GREEN}PASS${NC}: Base64 output is valid" +else + TESTS_RUN=$((TESTS_RUN + 1)) + TESTS_FAILED=$((TESTS_FAILED + 1)) + echo -e "${RED}FAIL${NC}: Base64 output is invalid" +fi + +# Verify decoded base64 is valid PNG +decoded=$(echo "$base64_output" | base64 -d 2>/dev/null) +if echo "$decoded" | head -c 8 | od -A n -t x1 | tr -d ' \n' | grep -qi "89504e470d0a1a0a"; then + TESTS_RUN=$((TESTS_RUN + 1)) + TESTS_PASSED=$((TESTS_PASSED + 1)) + echo -e "${GREEN}PASS${NC}: Decoded base64 contains PNG signature" +else + TESTS_RUN=$((TESTS_RUN + 1)) + TESTS_FAILED=$((TESTS_FAILED + 1)) + echo -e "${RED}FAIL${NC}: Decoded base64 does not contain PNG signature" +fi + +print_summary