Skip to content

Instantly share code, notes, and snippets.

@piotrkochan
Created September 29, 2025 18:50
Show Gist options
  • Save piotrkochan/1eb15d8ecb85c866e716bd07ee48d203 to your computer and use it in GitHub Desktop.
Save piotrkochan/1eb15d8ecb85c866e716bd07ee48d203 to your computer and use it in GitHub Desktop.
Photorec Image Filter Test Suite
Starting PhotoRec Image Filter Test Suite...
Running baseline: No filters - reference test...
PhotoRec phase: Starting recovery...
PhotoRec phase: Completed in 8s
Analysis phase: Validating results...
-> JPG: 21, PNG: 5835, Total: 5856 files (PhotoRec: 8s, Analysis: 0s, Total: 8s) - PASSED
Analyzing baseline files to determine test ranges...
File size analysis: 0k - 134k (25%=0.4k, 50%=0.6k, 75%=1k)
Analyzing image dimensions...
Dimension analysis: 1x6 - 3840x1200 (median: 200x150)
Pixel analysis: 6 - 4608000 (median: 30000)
Running test_filesize_min: File size ≥0.6k (median+)...
PhotoRec phase: Starting recovery...
PhotoRec phase: Completed in 8s
Analysis phase: Validating results...
-> JPG: 15, PNG: 3020, Total: 3035 files (PhotoRec: 8s, Analysis: 0s, Total: 8s) - PASSED
Running test_filesize_max: File size ≤1k (75th percentile)...
PhotoRec phase: Starting recovery...
PhotoRec phase: Completed in 7s
Analysis phase: Validating results...
-> JPG: 1, PNG: 4154, Total: 4155 files (PhotoRec: 7s, Analysis: 0s, Total: 7s) - PASSED
Running test_filesize_range: File size 0.4k-1k (IQR)...
PhotoRec phase: Starting recovery...
PhotoRec phase: Completed in 7s
Analysis phase: Validating results...
-> JPG: 1, PNG: 2457, Total: 2458 files (PhotoRec: 7s, Analysis: 0s, Total: 7s) - PASSED
Running test_decimal_filesize: Decimal file size 0.6k-1k...
PhotoRec phase: Starting recovery...
PhotoRec phase: Completed in 7s
Analysis phase: Validating results...
-> JPG: 1, PNG: 1339, Total: 1340 files (PhotoRec: 7s, Analysis: 0s, Total: 7s) - PASSED
Running test_large_filesize: Large files ≥100KB...
PhotoRec phase: Starting recovery...
PhotoRec phase: Completed in 6s
Analysis phase: Validating results...
-> JPG: 3, PNG: 1, Total: 4 files (PhotoRec: 6s, Analysis: 0s, Total: 6s) - PASSED
Running test_dimensions_min: Dimensions ≥200x150 (median+)...
PhotoRec phase: Starting recovery...
PhotoRec phase: Completed in 7s
Analysis phase: Validating results...
-> JPG: 7, PNG: 298, Total: 305 files (PhotoRec: 7s, Analysis: 0s, Total: 7s) - PASSED
Running test_dimensions_range: Dimensions 200-400 x 150-300...
PhotoRec phase: Starting recovery...
PhotoRec phase: Completed in 2s
Analysis phase: Validating results...
-> JPG: 0, PNG: 110, Total: 110 files (PhotoRec: 2s, Analysis: 0s, Total: 2s) - PASSED
Running test_small_dimensions: Small dimensions ≤100x100...
PhotoRec phase: Starting recovery...
PhotoRec phase: Completed in 1s
Analysis phase: Validating results...
-> JPG: 5, PNG: 418, Total: 423 files (PhotoRec: 1s, Analysis: 0s, Total: 1s) - PASSED
Running test_square_images: Square-ish images 100-500x100-500...
PhotoRec phase: Starting recovery...
PhotoRec phase: Completed in 2s
Analysis phase: Validating results...
-> JPG: 0, PNG: 244, Total: 244 files (PhotoRec: 2s, Analysis: 0s, Total: 2s) - PASSED
Running test_restrictive: Restrictive filter ≥800x600...
PhotoRec phase: Starting recovery...
PhotoRec phase: Completed in 6s
Analysis phase: Validating results...
-> JPG: 6, PNG: 24, Total: 30 files (PhotoRec: 6s, Analysis: 0s, Total: 6s) - PASSED
Running test_pixels_min: Pixels ≥30000 (median+)...
PhotoRec phase: Starting recovery...
PhotoRec phase: Completed in 7s
Analysis phase: Validating results...
-> JPG: 7, PNG: 336, Total: 343 files (PhotoRec: 7s, Analysis: 0s, Total: 7s) - PASSED
Running test_pixels_range: Pixels 30000-100000 (median-75th)...
PhotoRec phase: Starting recovery...
PhotoRec phase: Completed in 2s
Analysis phase: Validating results...
-> JPG: 0, PNG: 154, Total: 154 files (PhotoRec: 2s, Analysis: 0s, Total: 2s) - PASSED
Running test_pixels_format: Pixels WIDTHxHEIGHT format 200x150-400x300...
PhotoRec phase: Starting recovery...
PhotoRec phase: Completed in 2s
Analysis phase: Validating results...
-> JPG: 0, PNG: 165, Total: 165 files (PhotoRec: 2s, Analysis: 0s, Total: 2s) - PASSED
Running test_high_resolution: High resolution ≥1M pixels...
PhotoRec phase: Starting recovery...
PhotoRec phase: Completed in 6s
Analysis phase: Validating results...
-> JPG: 6, PNG: 5, Total: 11 files (PhotoRec: 6s, Analysis: 0s, Total: 6s) - PASSED
Running test_combined: Combined filters (filesize + dimensions)...
PhotoRec phase: Starting recovery...
PhotoRec phase: Completed in 7s
Analysis phase: Validating results...
-> JPG: 7, PNG: 295, Total: 302 files (PhotoRec: 7s, Analysis: 0s, Total: 7s) - PASSED
Running test_mixed_units: Mixed: 50KB-2MB files, width ≥300px...
PhotoRec phase: Starting recovery...
PhotoRec phase: Completed in 11s
Analysis phase: Validating results...
-> JPG: 5, PNG: 5, Total: 10 files (PhotoRec: 11s, Analysis: 0s, Total: 11s) - PASSED
All tests completed!
=== TEST SUMMARY ===
Total tests: 17
Passed: 17
Failed: 0
Baseline files: 5856
Total execution time: 96.4s
#!/usr/bin/env python3
"""
PhotoRec Image Filter Test Suite
Comprehensive test suite for PhotoRec's image filtering functionality.
Tests file size, dimension, and pixel count filters across JPG and PNG formats.
"""
import os
import sys
import subprocess
import shutil
import time
import re
from pathlib import Path
from dataclasses import dataclass
from typing import List, Dict, Optional, Tuple
@dataclass
class TestResult:
name: str
description: str
args: str
jpg_count: int
png_count: int
total_count: int
duration: float
photorec_duration: float
analysis_duration: float
status: str
failed_files: List[str] = None
class PhotoRecTestSuite:
def __init__(self, disk_image: str, output_dir: str, photorec_bin: str, debug: bool = False):
self.disk_image = Path(disk_image)
self.output_dir = Path(output_dir)
self.photorec_bin = Path(photorec_bin)
self.results: List[TestResult] = []
self.baseline_count = 0
self.debug = debug
# Validate inputs
if not self.disk_image.exists():
raise FileNotFoundError(f"Disk image not found: {disk_image}")
if not self.photorec_bin.exists():
raise FileNotFoundError(f"PhotoRec binary not found: {photorec_bin}")
# Ensure output directory exists
self.output_dir.mkdir(parents=True, exist_ok=True)
def cleanup_outputs(self):
"""Clean all output directories"""
if self.output_dir.exists():
shutil.rmtree(self.output_dir)
self.output_dir.mkdir(parents=True, exist_ok=True)
def run_photorec(self, test_name: str, args: str) -> Tuple[float, int, int]:
"""Run PhotoRec with given arguments and return timing info and counts"""
# Clean previous results (including .1, .2, etc. suffixes)
for path in self.output_dir.glob(f"{test_name}*"):
if path.is_dir():
shutil.rmtree(path)
# Remove PhotoRec session files that might interfere
session_files = ["photorec.ses", "photorec.cfg"]
for session_file in session_files:
if os.path.exists(session_file):
os.remove(session_file)
# Construct PhotoRec command
cmd_args = "partition_none,fileopt,everything,disable,jpg,enable,png,enable"
if args:
cmd_args += "," + args
cmd_args += ",search"
cmd = [
str(self.photorec_bin),
"/log",
"/d", str(self.output_dir / test_name),
"/cmd", str(self.disk_image),
cmd_args
]
if self.debug:
print(f" DEBUG: PhotoRec command: {' '.join(cmd)}")
print(f" PhotoRec phase: Starting recovery...")
start_time = time.time()
# Run PhotoRec
if self.debug:
subprocess.run(cmd)
else:
with open(os.devnull, 'w') as devnull:
subprocess.run(cmd, stdout=devnull, stderr=devnull)
end_time = time.time()
duration = end_time - start_time
print(f" PhotoRec phase: Completed in {duration:.0f}s")
# Count recovered files
pattern = test_name + ".*"
jpg_files = list(self.output_dir.glob(f"{pattern}/*.jpg"))
png_files = list(self.output_dir.glob(f"{pattern}/*.png"))
return duration, len(jpg_files), len(png_files)
def get_image_dimensions(self, image_path: Path) -> Optional[Tuple[int, int]]:
"""Get image dimensions using identify command"""
try:
result = subprocess.run(
["identify", "-ping", "-format", "%wx%h", str(image_path)],
capture_output=True, text=True, timeout=10
)
if result.returncode == 0:
dimensions = result.stdout.strip()
if 'x' in dimensions:
w, h = dimensions.split('x')
return int(w), int(h)
except (subprocess.TimeoutExpired, subprocess.CalledProcessError, ValueError):
pass
return None
def validate_filter(self, files: List[Path], args: str, filter_type: str) -> List[str]:
"""Generic validation method for all filter types"""
failed_files = []
validators = {
'size': self._validate_filesize,
'width': self._validate_dimensions,
'height': self._validate_dimensions,
'pixels': self._validate_pixels
}
# Check which filters are active in args
for ftype, validator in validators.items():
if ftype in args and (filter_type == ftype or
(filter_type in ['width', 'height'] and ftype in ['width', 'height'])):
failed_files.extend(validator(files, args))
return failed_files
def _validate_filesize(self, files: List[Path], args: str) -> List[str]:
"""Validate files against filesize criteria"""
failed_files = []
filesize_match = re.search(r',size,([^,]+)', args)
if not filesize_match:
return failed_files
min_size, max_size = self._parse_range(filesize_match.group(1), self._parse_size)
for file_path in files:
try:
file_size = file_path.stat().st_size
if not (min_size <= file_size <= max_size):
failed_files.append(f"{file_path.name}: {file_size} bytes (expected {min_size}-{max_size})")
except OSError:
failed_files.append(f"{file_path.name}: Could not get file size")
return failed_files
def _validate_dimensions(self, files: List[Path], args: str) -> List[str]:
"""Validate files against dimension criteria"""
failed_files = []
# Parse width and height ranges
ranges = {}
for dim in ['width', 'height']:
match = re.search(f'{dim},([^,]+)', args)
if match:
ranges[dim] = self._parse_range(match.group(1))
else:
ranges[dim] = (0, float('inf'))
for file_path in files:
dimensions = self.get_image_dimensions(file_path)
if dimensions is None:
failed_files.append(f"{file_path.name}: Could not get dimensions")
continue
width, height = dimensions
width_ok = ranges['width'][0] <= width <= ranges['width'][1]
height_ok = ranges['height'][0] <= height <= ranges['height'][1]
if not (width_ok and height_ok):
failed_files.append(
f"{file_path.name}: {width}x{height} "
f"(expected w:{ranges['width'][0]}-{ranges['width'][1]}, "
f"h:{ranges['height'][0]}-{ranges['height'][1]})"
)
return failed_files
def _validate_pixels(self, files: List[Path], args: str) -> List[str]:
"""Validate files against pixel criteria"""
failed_files = []
pixels_match = re.search(r'pixels,([^,]+)', args)
if not pixels_match:
return failed_files
if 'x' in pixels_match.group(1):
min_pixels, max_pixels = self._parse_range(pixels_match.group(1), self._parse_pixel_value)
else:
min_pixels, max_pixels = self._parse_range(pixels_match.group(1))
for file_path in files:
dimensions = self.get_image_dimensions(file_path)
if dimensions is None:
failed_files.append(f"{file_path.name}: Could not get dimensions")
continue
width, height = dimensions
pixels = width * height
if not (min_pixels <= pixels <= max_pixels):
failed_files.append(
f"{file_path.name}: {width}x{height}={pixels} pixels "
f"(expected {min_pixels}-{max_pixels})"
)
return failed_files
def _parse_size(self, size_str: str) -> int:
"""Parse size string like '10k', '1.5m' into bytes"""
if not size_str:
return 0
size_str = size_str.lower()
multipliers = {'k': 1024, 'm': 1024**2, 'g': 1024**3}
for suffix, mult in multipliers.items():
if size_str.endswith(suffix):
return int(float(size_str[:-1]) * mult)
return int(size_str)
def _parse_range(self, range_arg: str, parser_func=int) -> Tuple:
"""Generic range parser for dimensions, sizes, or pixels"""
if range_arg.startswith('-'):
return 0, parser_func(range_arg[1:])
elif range_arg.endswith('-'):
return parser_func(range_arg[:-1]), float('inf')
elif '-' in range_arg:
min_val, max_val = range_arg.split('-', 1)
return (parser_func(min_val) if min_val else 0,
parser_func(max_val) if max_val else float('inf'))
else:
val = parser_func(range_arg)
return val, val
def _parse_pixel_value(self, pixel_str: str) -> int:
"""Parse pixel value in WIDTHxHEIGHT format or numeric"""
if 'x' in pixel_str:
w, h = pixel_str.split('x')
return int(w) * int(h)
return int(pixel_str)
def run_test(self, test_name: str, args: str, description: str) -> TestResult:
"""Run a single test and return results"""
print(f"Running {test_name}: {description}...")
# Run PhotoRec
start_time = time.time()
photorec_duration, jpg_count, png_count = self.run_photorec(test_name, args)
# Analysis phase
print(" Analysis phase: Validating results...")
analysis_start = time.time()
total_count = jpg_count + png_count
failed_files = []
# Get all recovered files
pattern = test_name + ".*"
all_files = (list(self.output_dir.glob(f"{pattern}/*.jpg")) +
list(self.output_dir.glob(f"{pattern}/*.png")))
# Validate based on filter type
if test_name == "baseline":
self.baseline_count = total_count
status = "PASSED"
elif any(f in args for f in ["size,", "width", "height", "pixels"]):
filter_type = next((f.rstrip(',') for f in ["size", "width", "height", "pixels"] if f in args), "size")
failed_files = self.validate_filter(all_files, args, filter_type)
status = "PASSED" if len(failed_files) == 0 and total_count > 0 else "FAILED"
else:
status = "PASSED" if total_count == 0 else "FAILED"
analysis_duration = time.time() - analysis_start
total_duration = time.time() - start_time
print(f" -> JPG: {jpg_count}, PNG: {png_count}, Total: {total_count} files "
f"(PhotoRec: {photorec_duration:.0f}s, Analysis: {analysis_duration:.0f}s, "
f"Total: {total_duration:.0f}s) - {status}")
if failed_files:
print(f" -> Failed files: {len(failed_files)}")
for failure in failed_files[:5]: # Show first 5 failures
print(f" {failure}")
if len(failed_files) > 5:
print(f" ... and {len(failed_files) - 5} more")
result = TestResult(
name=test_name,
description=description,
args=args,
jpg_count=jpg_count,
png_count=png_count,
total_count=total_count,
duration=total_duration,
photorec_duration=photorec_duration,
analysis_duration=analysis_duration,
status=status,
failed_files=failed_files
)
self.results.append(result)
return result
def analyze_baseline(self) -> Dict:
"""Analyze baseline files to determine test ranges"""
print("Analyzing baseline files to determine test ranges...")
baseline_files = (list(self.output_dir.glob("baseline.*/*.jpg")) +
list(self.output_dir.glob("baseline.*/*.png")))
if not baseline_files:
print(" Using fallback values - no baseline files found")
return {
'p25_kb': 5, 'p50_kb': 10, 'p75_kb': 20,
'p50_width': 200, 'p50_height': 150,
'p75_width': 400, 'p75_height': 300,
'p50_pixels': 30000, 'p75_pixels': 100000
}
# Analyze file sizes
sizes = [f.stat().st_size for f in baseline_files if f.exists()]
sizes.sort()
def format_kb(bytes_val):
return f"{bytes_val/1024:.1f}k" if bytes_val < 1024 else f"{bytes_val//1024}k"
if sizes:
percentiles = [sizes[i] for i in [len(sizes)//4, len(sizes)//2, 3*len(sizes)//4]]
p25_kb, p50_kb, p75_kb = map(format_kb, percentiles)
min_kb, max_kb = sizes[0] // 1024, sizes[-1] // 1024
else:
min_kb, p25_kb, p50_kb, p75_kb, max_kb = 1, "0.5k", "1k", "2k", 100
print(f" File size analysis: {min_kb}k - {max_kb}k "
f"(25%={p25_kb}, 50%={p50_kb}, 75%={p75_kb})")
# Analyze dimensions (sample for performance)
print(" Analyzing image dimensions...")
dimensions_data = [self.get_image_dimensions(f) for f in baseline_files[:100]]
valid_dims = [d for d in dimensions_data if d]
if valid_dims:
widths, heights = zip(*valid_dims)
pixels = [w * h for w, h in valid_dims]
def get_percentiles(data, defaults):
sorted_data = sorted(data)
p50 = max(defaults[0], sorted_data[len(sorted_data)//2])
p75 = max(defaults[1], sorted_data[3*len(sorted_data)//4])
return p50, p75
p50_width, p75_width = get_percentiles(widths, (200, 400))
p50_height, p75_height = get_percentiles(heights, (150, 300))
p50_pixels, p75_pixels = get_percentiles(pixels, (30000, 100000))
print(f" Dimension analysis: {min(widths)}x{min(heights)} - "
f"{max(widths)}x{max(heights)} (median: {p50_width}x{p50_height})")
print(f" Pixel analysis: {min(pixels)} - {max(pixels)} (median: {p50_pixels})")
else:
p50_width, p75_width = 200, 400
p50_height, p75_height = 150, 300
p50_pixels, p75_pixels = 30000, 100000
print(" Using fallback dimension values")
return {
'p25_kb': p25_kb, 'p50_kb': p50_kb, 'p75_kb': p75_kb,
'p50_width': p50_width, 'p50_height': p50_height,
'p75_width': p75_width, 'p75_height': p75_height,
'p50_pixels': p50_pixels, 'p75_pixels': p75_pixels
}
def get_all_test_definitions(self, ranges):
"""Get all test definitions"""
r = ranges # Short alias
base_cmd = "imagesize"
tests = {
"baseline": ("", "No filters - reference test"),
# File size tests
"test_filesize_min": (f"{base_cmd},size,{r['p50_kb']}-", f"File size ≥{r['p50_kb']} (median+)"),
"test_filesize_max": (f"{base_cmd},size,-{r['p75_kb']}", f"File size ≤{r['p75_kb']} (75th percentile)"),
"test_filesize_range": (f"{base_cmd},size,{r['p25_kb']}-{r['p75_kb']}", f"File size {r['p25_kb']}-{r['p75_kb']} (IQR)"),
"test_decimal_filesize": (f"{base_cmd},size,{r['p50_kb']}-{r['p75_kb']}", f"Decimal file size {r['p50_kb']}-{r['p75_kb']}"),
"test_large_filesize": (f"{base_cmd},size,100k-", "Large files ≥100KB"),
# Dimension tests
"test_dimensions_min": (f"{base_cmd},width,{r['p50_width']}-,height,{r['p50_height']}-", f"Dimensions ≥{r['p50_width']}x{r['p50_height']} (median+)"),
"test_dimensions_range": (f"{base_cmd},width,{r['p50_width']}-{r['p75_width']},height,{r['p50_height']}-{r['p75_height']}", f"Dimensions {r['p50_width']}-{r['p75_width']} x {r['p50_height']}-{r['p75_height']}"),
"test_small_dimensions": (f"{base_cmd},width,-100,height,-100", "Small dimensions ≤100x100"),
"test_square_images": (f"{base_cmd},width,100-500,height,100-500", "Square-ish images 100-500x100-500"),
"test_restrictive": (f"{base_cmd},width,{r['p75_width'] * 2}-,height,{r['p75_height'] * 2}-", f"Restrictive filter ≥{r['p75_width'] * 2}x{r['p75_height'] * 2}"),
# Pixel tests
"test_pixels_min": (f"{base_cmd},pixels,{r['p50_pixels']}-", f"Pixels ≥{r['p50_pixels']} (median+)"),
"test_pixels_range": (f"{base_cmd},pixels,{r['p50_pixels']}-{r['p75_pixels']}", f"Pixels {r['p50_pixels']}-{r['p75_pixels']} (median-75th)"),
"test_pixels_format": (f"{base_cmd},pixels,{r['p50_width']}x{r['p50_height']}-{r['p75_width']}x{r['p75_height']}", f"Pixels WIDTHxHEIGHT format {r['p50_width']}x{r['p50_height']}-{r['p75_width']}x{r['p75_height']}"),
"test_high_resolution": (f"{base_cmd},pixels,1000000-", "High resolution ≥1M pixels"),
# Combined tests
"test_combined": (f"{base_cmd},size,{r['p25_kb']}-,width,{r['p50_width']}-,height,{r['p50_height']}-", "Combined filters (filesize + dimensions)"),
"test_mixed_units": (f"{base_cmd},size,50k-2m,width,300-", "Mixed: 50KB-2MB files, width ≥300px")
}
return tests
def run_specific_tests(self, test_names: List[str]):
"""Run specific tests by name"""
print(f"Starting PhotoRec Image Filter Test Suite (running: {', '.join(test_names)})...")
# Always run baseline first if not already specified
if "baseline" not in test_names:
self.run_test("baseline", "", "No filters - reference test")
# Analyze baseline to determine ranges
ranges = self.analyze_baseline()
# Get test definitions
test_definitions = self.get_all_test_definitions(ranges)
# Run specified tests
for test_name in test_names:
if test_name in test_definitions:
args, description = test_definitions[test_name]
self.run_test(test_name, args, description)
else:
print(f"Warning: Unknown test '{test_name}' - skipping")
available_tests = list(test_definitions.keys())
print(f"Available tests: {', '.join(available_tests)}")
print("\nSelected tests completed!")
def run_all_tests(self):
"""Run the complete test suite"""
print("Starting PhotoRec Image Filter Test Suite...")
# Baseline test
self.run_test("baseline", "", "No filters - reference test")
# Analyze baseline to determine ranges
ranges = self.analyze_baseline()
# Get test definitions and run all
test_definitions = self.get_all_test_definitions(ranges)
for test_name, (args, description) in test_definitions.items():
if test_name != "baseline": # Already run
self.run_test(test_name, args, description)
print("\nAll tests completed!")
def print_summary(self):
"""Print test summary"""
total_tests = len(self.results)
passed_tests = sum(1 for r in self.results if r.status == "PASSED")
failed_tests = total_tests - passed_tests
print(f"\n=== TEST SUMMARY ===")
print(f"Total tests: {total_tests}")
print(f"Passed: {passed_tests}")
print(f"Failed: {failed_tests}")
if failed_tests > 0:
print(f"\nFAILED TESTS:")
for result in self.results:
if result.status == "FAILED":
print(f" - {result.name}: {result.description}")
if result.failed_files:
print(f" Failed files: {len(result.failed_files)}")
print(f"\nBaseline files: {self.baseline_count}")
def main():
import argparse
parser = argparse.ArgumentParser(description='PhotoRec Image Filter Test Suite')
parser.add_argument('tests', nargs='*', help='Specific tests to run (e.g., test_filesize_min test_filesize_max). If none specified, runs all tests.')
parser.add_argument('--debug', action='store_true', help='Enable debug mode (show PhotoRec commands and output)')
parser.add_argument('--list', action='store_true', help='List all available tests')
parser.add_argument('--disk-image', default=os.path.expanduser("~/disk_30gb.img"), help='Path to disk image')
parser.add_argument('--output-dir', default=os.path.expanduser("~/outputs"), help='Output directory')
parser.add_argument('--photorec-bin', default="./src/photorec", help='PhotoRec binary path')
args = parser.parse_args()
# Configuration
DISK_IMAGE = args.disk_image
OUTPUT_DIR = args.output_dir
PHOTOREC_BIN = args.photorec_bin
try:
# Initialize test suite
suite = PhotoRecTestSuite(DISK_IMAGE, OUTPUT_DIR, PHOTOREC_BIN, debug=args.debug)
if args.list:
# Show available tests
ranges = {'p25_kb': 5, 'p50_kb': 10, 'p75_kb': 20, 'p50_width': 200, 'p50_height': 150, 'p75_width': 400, 'p75_height': 300, 'p50_pixels': 30000, 'p75_pixels': 100000}
test_definitions = suite.get_all_test_definitions(ranges)
print("Available tests:")
for test_name, (args, description) in test_definitions.items():
print(f" {test_name}: {description}")
return
# Clean previous results
suite.cleanup_outputs()
start_time = time.time()
if args.tests:
# Run specific tests
suite.run_specific_tests(args.tests)
else:
# Run all tests
suite.run_all_tests()
total_time = time.time() - start_time
# Print summary
suite.print_summary()
print(f"\nTotal execution time: {total_time:.1f}s")
except FileNotFoundError as e:
print(f"Error: {e}")
sys.exit(1)
except KeyboardInterrupt:
print("\nTest suite interrupted by user")
sys.exit(1)
except Exception as e:
print(f"Unexpected error: {e}")
sys.exit(1)
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment