#!/bin/bash # Media Importer Test Runner # Comprehensive testing framework based on Development.md specifications set -e SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" TEST_DIR="$SCRIPT_DIR/test" SOURCE_DIR="$TEST_DIR/source" DEST_DIR="$TEST_DIR/destination" SAMPLES_DIR="$SCRIPT_DIR/sample" # Fixed: was 'samples' but should be 'sample' MEDIA_IMPORTER="$SCRIPT_DIR/media-importer.sh" TEST_REPORTS_DIR="$SCRIPT_DIR/test_reports" # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color # Function to print colored output print_color() { local color="$1" local message="$2" echo -e "${color}${message}${NC}" } # Function to create test directories create_test_dirs() { print_color "$BLUE" "Creating test directories..." mkdir -p "$SOURCE_DIR" mkdir -p "$DEST_DIR" mkdir -p "$TEST_REPORTS_DIR" } # Function to clean test directory clean_test_dir() { print_color "$BLUE" "Cleaning test directory..." rm -rf "$TEST_DIR"/* } # Function to copy sample files copy_sample_files() { local source_pattern="$1" local target_dir="$2" if [[ -d "$SAMPLES_DIR/$source_pattern" ]]; then # Copy all files recursively from source to target directory find "$SAMPLES_DIR/$source_pattern" -type f \( -iname "*.jpg" -o -iname "*.jpeg" -o -iname "*.png" -o -iname "*.tiff" -o -iname "*.tif" -o -iname "*.cr2" -o -iname "*.nef" -o -iname "*.arw" -o -iname "*.dng" -o -iname "*.raw" -o -iname "*.mp4" -o -iname "*.mov" -o -iname "*.avi" -o -iname "*.mts" -o -iname "*.m2ts" -o -iname "*.mkv" -o -iname "*.wmv" -o -iname "*.3gp" -o -iname "*.m4v" \) -exec cp {} "$target_dir/" \; 2>/dev/null || true print_color "$GREEN" "Copied $source_pattern files to $target_dir" else print_color "$YELLOW" "Warning: $SAMPLES_DIR/$source_pattern not found. Path: $SAMPLES_DIR/$source_pattern" fi } # Function to capture directory state capture_state() { local dir_path="$1" local output_file="$2" local label="$3" print_color "$BLUE" "Capturing $label directory state..." if [[ -d "$dir_path" ]]; then find "$dir_path" -type f | sort > "$output_file" else echo "# Directory does not exist or is empty" > "$output_file" fi } # Function to run test command and capture output run_test_command() { local command="$1" local log_file="$2" print_color "$YELLOW" "Executing: $command" echo "$command" > "$log_file" echo "" >> "$log_file" echo "=== SCRIPT OUTPUT ===" >> "$log_file" # Run command and capture both stdout and stderr if eval "$command" >> "$log_file" 2>&1; then echo "" >> "$log_file" echo "=== COMMAND COMPLETED SUCCESSFULLY ===" >> "$log_file" return 0 else echo "" >> "$log_file" echo "=== COMMAND FAILED ===" >> "$log_file" return 1 fi } # Function to generate test report generate_test_report() { local test_name="$1" local scenario="$2" local objective="$3" local files_used="$4" local command="$5" local result_status="$6" # Determine success/failure prefix local status_prefix if [[ $result_status -eq 0 ]]; then status_prefix="s" else status_prefix="f" fi # Format date as YYYYMMDD local date_stamp=$(date '+%Y%m%d') # Create filename with new format: [s|f]_date_restul_numelei local report_file="$TEST_REPORTS_DIR/${status_prefix}_${date_stamp}_${test_name}.md" print_color "$BLUE" "Generating test report: $report_file" cat > "$report_file" << EOF # Test Report: $test_name ## Test Information - **Date**: $(date) - **Scenario**: $scenario - **Objective**: $objective - **Files Used**: $files_used ## Pre-Test State ### Source Directory Structure \`\`\` $(cat "$TEST_DIR/source_before.txt" 2>/dev/null || echo "# No pre-test source state captured") \`\`\` ### Destination Directory Structure \`\`\` $(cat "$TEST_DIR/dest_before.txt" 2>/dev/null || echo "# No pre-test destination state captured") \`\`\` ## Test Execution ### Command Used \`\`\`bash $command \`\`\` ### Script Output \`\`\` $(cat "$TEST_DIR/import_log.txt" 2>/dev/null || echo "# No script output captured") \`\`\` ## Post-Test State ### Source Directory Structure \`\`\` $(cat "$TEST_DIR/source_after.txt" 2>/dev/null || echo "# No post-test source state captured") \`\`\` ### Destination Directory Structure \`\`\` $(cat "$TEST_DIR/dest_after.txt" 2>/dev/null || echo "# No post-test destination state captured") \`\`\` ## Analysis and Verification ### Expected Results - Files should be processed according to the test scenario - No data loss should occur - Proper error handling for edge cases ### Actual Results - Test execution completed with status: $result_status ### Issues Found - [ ] No issues detected - [ ] Issues found (see notes below) ### Protections Verified - [ ] Destination exclusion working - [ ] Move confirmation functional - [ ] No data loss detected - [ ] UTC conversion correct (for QuickTime files) - [ ] Unimportable files handling (if applicable) ## Conclusion ### Test Result - [ ] PASSED - [ ] FAILED - [ ] PARTIAL (with notes) ### Notes Test completed as per Development.md specifications. ### Files Generated - \`test/source_before.txt\` - Pre-test source structure - \`test/dest_before.txt\` - Pre-test destination structure - \`test/source_after.txt\` - Post-test source structure - \`test/dest_after.txt\` - Post-test destination structure - \`test/import_log.txt\` - Full script execution log - \`test/test_report.md\` - This report EOF print_color "$GREEN" "Test report generated: $report_file" # Clean up old reports, keeping only the last 10 cleanup_old_reports } # Function to clean up old test reports, keeping only the last 10 cleanup_old_reports() { if [[ -d "$TEST_REPORTS_DIR" ]]; then # Count total reports local total_reports=$(find "$TEST_REPORTS_DIR" -name "*.md" | wc -l) if [[ $total_reports -gt 10 ]]; then print_color "$BLUE" "Cleaning up old test reports (keeping last 10)..." # Find and remove oldest reports, keeping the 10 most recent find "$TEST_REPORTS_DIR" -name "*.md" -type f -printf '%T@ %p\n' | \ sort -n | \ head -n -$((10)) | \ cut -d' ' -f2- | \ xargs -r rm local remaining=$(find "$TEST_REPORTS_DIR" -name "*.md" | wc -l) print_color "$GREEN" "Kept $remaining most recent test reports" fi fi } # Test Case 0: Basic Functionality Test run_basic_functionality_test() { print_color "$GREEN" "=== Running Test 0: Basic Functionality Test ===" clean_test_dir create_test_dirs # Setup test files copy_sample_files "media/sortable" "$SOURCE_DIR" # Capture pre-test state capture_state "$SOURCE_DIR" "$TEST_DIR/source_before.txt" "source" capture_state "$DEST_DIR" "$TEST_DIR/dest_before.txt" "destination" # Run test local command="\"$MEDIA_IMPORTER\" -s \"$SOURCE_DIR\" -d \"$DEST_DIR\" -v" local result=0 run_test_command "$command" "$TEST_DIR/import_log.txt" || result=$? # Capture post-test state capture_state "$SOURCE_DIR" "$TEST_DIR/source_after.txt" "source" capture_state "$DEST_DIR" "$TEST_DIR/dest_after.txt" "destination" # Generate report generate_test_report \ "Basic_Functionality" \ "Processing files with valid EXIF data" \ "Verify correct sorting and organization of media files" \ "Sample media files with EXIF data from samples/media/sortable/" \ "$command" \ "$result" } # Test Case 1: Unimportable Files Test run_unimportable_files_test() { print_color "$GREEN" "=== Running Test 1: Unimportable Files Test ===" clean_test_dir create_test_dirs # Setup test files - mix of sortable and unimportable copy_sample_files "media/sortable" "$SOURCE_DIR" copy_sample_files "media/unimportable" "$SOURCE_DIR" # Capture pre-test state capture_state "$SOURCE_DIR" "$TEST_DIR/source_before.txt" "source" capture_state "$DEST_DIR" "$TEST_DIR/dest_before.txt" "destination" # Run test (without --collect-unimportable flag) local command="\"$MEDIA_IMPORTER\" -s \"$SOURCE_DIR\" -d \"$DEST_DIR\" -v" local result=0 run_test_command "$command" "$TEST_DIR/import_log.txt" || result=$? # Capture post-test state capture_state "$SOURCE_DIR" "$TEST_DIR/source_after.txt" "source" capture_state "$DEST_DIR" "$TEST_DIR/dest_after.txt" "destination" # Generate report generate_test_report \ "Unimportable_Files_Handling" \ "Testing files without EXIF data in root and subfolders" \ "Verify proper handling of unimportable files without collection flag" \ "Mixed sortable and unimportable files from samples/media/" \ "$command" \ "$result" } # Test Case 2: Mixed Content Test run_mixed_content_test() { print_color "$GREEN" "=== Running Test 2: Mixed Content Test ===" clean_test_dir create_test_dirs # Setup separate folders for sortable vs unimportable mkdir -p "$SOURCE_DIR/sortable" "$SOURCE_DIR/unimportable" copy_sample_files "media/sortable" "$SOURCE_DIR/sortable" copy_sample_files "media/unimportable" "$SOURCE_DIR/unimportable" # Capture pre-test state capture_state "$SOURCE_DIR" "$TEST_DIR/source_before.txt" "source" capture_state "$DEST_DIR" "$TEST_DIR/dest_before.txt" "destination" # Run test local command="\"$MEDIA_IMPORTER\" -s \"$SOURCE_DIR\" -d \"$DEST_DIR\" -v" local result=0 run_test_command "$command" "$TEST_DIR/import_log.txt" || result=$? # Capture post-test state capture_state "$SOURCE_DIR" "$TEST_DIR/source_after.txt" "source" capture_state "$DEST_DIR" "$TEST_DIR/dest_after.txt" "destination" # Generate report generate_test_report \ "Mixed_Content" \ "Processing sortable and unimportable files in separate folders" \ "Verify cleanup behavior for folders with different file types" \ "Separate folders: sortable/ and unimportable/ with respective file types" \ "$command" \ "$result" } # Test Case 3: Safety Protections Test run_safety_protections_test() { print_color "$GREEN" "=== Running Test 3: Safety Protections Test ===" clean_test_dir create_test_dirs # Setup test files copy_sample_files "media/sortable" "$SOURCE_DIR" # Create a file in destination to test exclusion echo "test file" > "$DEST_DIR/test.txt" # Capture pre-test state capture_state "$SOURCE_DIR" "$TEST_DIR/source_before.txt" "source" capture_state "$DEST_DIR" "$TEST_DIR/dest_before.txt" "destination" # Run test local command="\"$MEDIA_IMPORTER\" -s \"$SOURCE_DIR\" -d \"$DEST_DIR\" -v" local result=0 run_test_command "$command" "$TEST_DIR/import_log.txt" || result=$? # Capture post-test state capture_state "$SOURCE_DIR" "$TEST_DIR/source_after.txt" "source" capture_state "$DEST_DIR" "$TEST_DIR/dest_after.txt" "destination" # Generate report generate_test_report \ "Safety_Protections" \ "Testing destination exclusion and move confirmation" \ "Verify data integrity protections prevent data loss" \ "Sample files with pre-existing destination content" \ "$command" \ "$result" } # Test Case 4: UTC Conversion Test run_utc_conversion_test() { print_color "$GREEN" "=== Running Test 4: UTC Conversion Test ===" clean_test_dir create_test_dirs # Setup test files (assuming QuickTime files are available) copy_sample_files "media/sortable" "$SOURCE_DIR" # Capture pre-test state capture_state "$SOURCE_DIR" "$TEST_DIR/source_before.txt" "source" capture_state "$DEST_DIR" "$TEST_DIR/dest_before.txt" "destination" # Run test local command="\"$MEDIA_IMPORTER\" -s \"$SOURCE_DIR\" -d \"$DEST_DIR\" -v" local result=0 run_test_command "$command" "$TEST_DIR/import_log.txt" || result=$? # Capture post-test state capture_state "$SOURCE_DIR" "$TEST_DIR/source_after.txt" "source" capture_state "$DEST_DIR" "$TEST_DIR/dest_after.txt" "destination" # Generate report generate_test_report \ "UTC_Conversion" \ "Testing UTC timestamp conversion for QuickTime files" \ "Verify correct UTC to local time conversion" \ "QuickTime/Apple media files with UTC timestamps" \ "$command" \ "$result" } # Test Case 5: Subdirectory Processing Test run_subdirectory_processing_test() { print_color "$GREEN" "=== Running Test 5: Subdirectory Processing Test ===" clean_test_dir create_test_dirs # Create nested directory structure mkdir -p "$SOURCE_DIR/level1/level2" copy_sample_files "media/sortable" "$SOURCE_DIR/level1/level2" # Capture pre-test state capture_state "$SOURCE_DIR" "$TEST_DIR/source_before.txt" "source" capture_state "$DEST_DIR" "$TEST_DIR/dest_before.txt" "destination" # Run test local command="\"$MEDIA_IMPORTER\" -s \"$SOURCE_DIR\" -d \"$DEST_DIR\" -v" local result=0 run_test_command "$command" "$TEST_DIR/import_log.txt" || result=$? # Capture post-test state capture_state "$SOURCE_DIR" "$TEST_DIR/source_after.txt" "source" capture_state "$DEST_DIR" "$TEST_DIR/dest_after.txt" "destination" # Generate report generate_test_report \ "Subdirectory_Processing" \ "Testing processing of files in nested subdirectories" \ "Verify recursive file discovery and processing" \ "Files in nested directory structure (level1/level2/)" \ "$command" \ "$result" } # Test Case 6: Keep Empty Directories Test run_keep_empty_dirs_test() { print_color "$GREEN" "=== Running Test 6: Keep Empty Directories Test ===" clean_test_dir create_test_dirs # Create directory structure with some empty dirs mkdir -p "$SOURCE_DIR/sortable" "$SOURCE_DIR/empty1" "$SOURCE_DIR/empty2" copy_sample_files "media/sortable" "$SOURCE_DIR/sortable" # Capture pre-test state capture_state "$SOURCE_DIR" "$TEST_DIR/source_before.txt" "source" capture_state "$DEST_DIR" "$TEST_DIR/dest_before.txt" "destination" # Run test with --keep-empty-dirs local command="\"$MEDIA_IMPORTER\" -s \"$SOURCE_DIR\" -d \"$DEST_DIR\" --keep-empty-dirs -v" local result=0 run_test_command "$command" "$TEST_DIR/import_log.txt" || result=$? # Capture post-test state capture_state "$SOURCE_DIR" "$TEST_DIR/source_after.txt" "source" capture_state "$DEST_DIR" "$TEST_DIR/dest_after.txt" "destination" # Generate report generate_test_report \ "Keep_Empty_Directories" \ "Testing --keep-empty-dirs functionality" \ "Verify empty directory preservation when flag is used" \ "Directory structure with sortable files and empty directories" \ "$command" \ "$result" } # Test Case 7: Source Only Test run_source_only_test() { print_color "$GREEN" "=== Running Test 7: Source Only Test ===" clean_test_dir create_test_dirs # Setup test files copy_sample_files "media/sortable" "$SOURCE_DIR" # Capture pre-test state capture_state "$SOURCE_DIR" "$TEST_DIR/source_before.txt" "source" capture_state "$SOURCE_DIR/sorted" "$TEST_DIR/dest_before.txt" "auto-destination" # Run test with only source parameter local command="\"$MEDIA_IMPORTER\" -s \"$SOURCE_DIR\" -v" local result=0 run_test_command "$command" "$TEST_DIR/import_log.txt" || result=$? # Capture post-test state capture_state "$SOURCE_DIR" "$TEST_DIR/source_after.txt" "source" capture_state "$SOURCE_DIR/sorted" "$TEST_DIR/dest_after.txt" "auto-destination" # Generate report generate_test_report \ "Source_Only_Test" \ "Testing processing with only source parameter" \ "Verify automatic creation of sorted subdirectory" \ "Sample media files, no explicit destination specified" \ "$command" \ "$result" } # Test Case 8: Destination Inside Source Test run_destination_inside_source_test() { print_color "$GREEN" "=== Running Test 8: Destination Inside Source Test ===" clean_test_dir create_test_dirs # Setup test files copy_sample_files "media/sortable" "$SOURCE_DIR" # Capture pre-test state capture_state "$SOURCE_DIR" "$TEST_DIR/source_before.txt" "source" capture_state "$SOURCE_DIR/sorted" "$TEST_DIR/dest_before.txt" "destination-inside-source" # Run test with destination explicitly inside source local command="\"$MEDIA_IMPORTER\" -s \"$SOURCE_DIR\" -d \"$SOURCE_DIR/sorted\" -v" local result=0 run_test_command "$command" "$TEST_DIR/import_log.txt" || result=$? # Guard 1: no files should remain outside sorted for sortable-only dataset local outside_count outside_count=$(find "$SOURCE_DIR" -type f ! -path "$SOURCE_DIR/sorted/*" | wc -l | tr -d ' ') if [[ "$outside_count" != "0" ]]; then echo "Destination exclusion check failed: found $outside_count file(s) outside sorted" >> "$TEST_DIR/import_log.txt" result=1 fi # Guard 2: destination exclusion should prevent sorted/sorted recursion local recursive_sorted_count recursive_sorted_count=$(find "$SOURCE_DIR/sorted" -type d -path "*/sorted/sorted*" 2>/dev/null | wc -l | tr -d ' ') if [[ "$recursive_sorted_count" != "0" ]]; then echo "Destination recursion check failed: found nested sorted/sorted directories" >> "$TEST_DIR/import_log.txt" result=1 fi # Capture post-test state capture_state "$SOURCE_DIR" "$TEST_DIR/source_after.txt" "source" capture_state "$SOURCE_DIR/sorted" "$TEST_DIR/dest_after.txt" "destination-inside-source" # Generate report generate_test_report \ "Destination_Inside_Source" \ "Testing explicit destination inside source directory" \ "Verify destination exclusion and no sorted/sorted recursion" \ "Sortable sample media with destination set to source/sorted" \ "$command" \ "$result" } # Test Case 9: Verify Mode Test run_verify_mode_test() { print_color "$GREEN" "=== Running Test 9: Verify Mode Test ===" clean_test_dir create_test_dirs # Setup test files copy_sample_files "media/sortable" "$SOURCE_DIR" # Capture pre-test state capture_state "$SOURCE_DIR" "$TEST_DIR/source_before.txt" "source" capture_state "$SOURCE_DIR/sorted" "$TEST_DIR/dest_before.txt" "auto-destination" local log_file="$TEST_DIR/import_log.txt" local result=0 : > "$log_file" local command_default="cd \"$SOURCE_DIR\" && \"$MEDIA_IMPORTER\" --dry-run" local command_strict="cd \"$SOURCE_DIR\" && \"$MEDIA_IMPORTER\" --dry-run --verify-mode strict" local command="$command_default && $command_strict" echo "$command_default" >> "$log_file" echo "" >> "$log_file" echo "=== DEFAULT VERIFY MODE OUTPUT ===" >> "$log_file" if eval "$command_default" >> "$log_file" 2>&1; then if ! grep -q "Verify mode:[[:space:]]*size" "$log_file"; then echo "Expected default verify mode 'size' not found in output" >> "$log_file" result=1 fi else result=1 fi echo "" >> "$log_file" echo "$command_strict" >> "$log_file" echo "" >> "$log_file" echo "=== STRICT VERIFY MODE OUTPUT ===" >> "$log_file" if eval "$command_strict" >> "$log_file" 2>&1; then if ! grep -q "Verify mode:[[:space:]]*strict" "$log_file"; then echo "Expected strict verify mode not found in output" >> "$log_file" result=1 fi else result=1 fi if [[ $result -eq 0 ]]; then echo "" >> "$log_file" echo "=== COMMAND COMPLETED SUCCESSFULLY ===" >> "$log_file" else echo "" >> "$log_file" echo "=== COMMAND FAILED ===" >> "$log_file" fi # Capture post-test state capture_state "$SOURCE_DIR" "$TEST_DIR/source_after.txt" "source" capture_state "$SOURCE_DIR/sorted" "$TEST_DIR/dest_after.txt" "auto-destination" # Generate report generate_test_report \ "Verify_Mode" \ "Testing verify-mode defaults and strict override" \ "Verify default mode is size and strict mode is accepted" \ "Sample sortable media in source-only dry-run mode" \ "$command" \ "$result" } # Function to show menu show_menu() { echo "" print_color "$GREEN" "Media Importer Test Runner" echo "==========================" echo "" echo "Available Tests:" echo "0. Basic Functionality Test" echo "1. Unimportable Files Test" echo "2. Mixed Content Test" echo "3. Safety Protections Test" echo "4. UTC Conversion Test" echo "5. Subdirectory Processing Test" echo "6. Keep Empty Directories Test" echo "7. Source Only Test" echo "8. Destination Inside Source Test" echo "9. Verify Mode Test" echo "10. Run All Tests" echo "q. Quit" echo "" echo -n "Select test to run (0-10, q to quit): " } # Function to run all tests run_all_tests() { print_color "$GREEN" "Running all tests sequentially..." run_basic_functionality_test echo "" run_unimportable_files_test echo "" run_mixed_content_test echo "" run_safety_protections_test echo "" run_utc_conversion_test echo "" run_subdirectory_processing_test echo "" run_keep_empty_dirs_test echo "" run_source_only_test echo "" run_destination_inside_source_test echo "" run_verify_mode_test print_color "$GREEN" "All tests completed!" } # Main menu loop main() { while true; do show_menu read -r choice case $choice in 0) run_basic_functionality_test ;; 1) run_unimportable_files_test ;; 2) run_mixed_content_test ;; 3) run_safety_protections_test ;; 4) run_utc_conversion_test ;; 5) run_subdirectory_processing_test ;; 6) run_keep_empty_dirs_test ;; 7) run_source_only_test ;; 8) run_destination_inside_source_test ;; 9) run_verify_mode_test ;; 10) run_all_tests ;; q|Q) print_color "$GREEN" "Exiting test runner." exit 0 ;; *) print_color "$RED" "Invalid choice. Please select 0-10 or q to quit." ;; esac echo "" echo -n "Press Enter to continue..." read -r done } # Check if script exists if [[ ! -f "$MEDIA_IMPORTER" ]]; then print_color "$RED" "Error: $MEDIA_IMPORTER not found!" exit 1 fi # Check if samples directory exists if [[ ! -d "$SAMPLES_DIR" ]]; then print_color "$YELLOW" "Warning: $SAMPLES_DIR not found. Some tests may not work properly." fi # Run main menu if no arguments provided if [[ $# -eq 0 ]]; then main else # Handle command line arguments case "$1" in "basic"|"0") run_basic_functionality_test ;; "unimportable"|"1") run_unimportable_files_test ;; "mixed"|"2") run_mixed_content_test ;; "safety"|"3") run_safety_protections_test ;; "utc"|"4") run_utc_conversion_test ;; "subdir"|"5") run_subdirectory_processing_test ;; "keep-empty"|"6") run_keep_empty_dirs_test ;; "source-only"|"7") run_source_only_test ;; "dest-in-source"|"8") run_destination_inside_source_test ;; "verify-mode"|"9") run_verify_mode_test ;; "all"|"10") run_all_tests ;; "clean") clean_test_dir ;; "setup") create_test_dirs ;; *) print_color "$RED" "Unknown test: $1" echo "Usage: $0 [basic|unimportable|mixed|safety|utc|subdir|keep-empty|source-only|dest-in-source|verify-mode|all|clean|setup] or [0-10]" exit 1 ;; esac fi