#!/usr/bin/env python3 """ Test that mimics the exact /api/compare-capacity flow. Run with: python test_compare_capacity_flow.py ./xx_2.jpg """ import sys import io import gc import json import time print("=" * 60) print("COMPARE-CAPACITY FLOW TEST") print("=" * 60) if len(sys.argv) < 2: print("Usage: python test_compare_capacity_flow.py ") sys.exit(1) image_path = sys.argv[1] # Read the file with open(image_path, 'rb') as f: carrier_data = f.read() print(f"Loaded {len(carrier_data)} bytes from {image_path}") # Import everything like Flask does print("\n[1] Importing modules...") from PIL import Image import numpy as np try: import jpegio as jio HAS_JPEGIO = True print(f" jpegio: available") except ImportError: HAS_JPEGIO = False print(f" jpegio: NOT available") try: from scipy.fft import dct, idct print(f" scipy.fft: available") except ImportError: from scipy.fftpack import dct, idct print(f" scipy.fftpack: available (fallback)") print(" Imports complete") # Simulate the compare_modes function print("\n[2] Opening image (1st time - for dimensions)...") img1 = Image.open(io.BytesIO(carrier_data)) width, height = img1.size print(f" Size: {width}x{height}") img1.close() print(" Closed img1") gc.collect() print("\n[3] Opening image (2nd time - for LSB capacity)...") img2 = Image.open(io.BytesIO(carrier_data)) num_pixels = img2.size[0] * img2.size[1] lsb_bytes = (num_pixels * 3) // 8 - 69 print(f" LSB capacity: {lsb_bytes} bytes") img2.close() print(" Closed img2") gc.collect() print("\n[4] Opening image (3rd time - for DCT capacity)...") img3 = Image.open(io.BytesIO(carrier_data)) w, h = img3.size blocks_x = w // 8 blocks_y = h // 8 total_blocks = blocks_x * blocks_y dct_bits = total_blocks * 16 dct_bytes = dct_bits // 8 - 10 print(f" DCT capacity: {dct_bytes} bytes ({total_blocks} blocks)") img3.close() print(" Closed img3") gc.collect() print("\n[5] Building response dict...") response = { 'success': True, 'width': width, 'height': height, 'lsb': { 'capacity_bytes': lsb_bytes, 'capacity_kb': round(lsb_bytes / 1024, 1), 'output': 'PNG', }, 'dct': { 'capacity_bytes': dct_bytes, 'capacity_kb': round(dct_bytes / 1024, 1), 'output': 'JPEG', 'available': True, 'ratio': round(dct_bytes / lsb_bytes * 100, 1), } } print(f" Response built") print("\n[6] Serializing to JSON...") json_str = json.dumps(response) print(f" JSON length: {len(json_str)} bytes") print(f" Content: {json_str[:200]}...") print("\n[7] Simulating Flask response completion...") # In Flask, after the response is sent, Python may garbage collect del carrier_data del response del json_str gc.collect() print(" GC after response simulation") print("\n[8] Additional cleanup (simulating request end)...") gc.collect() gc.collect() print(" Multiple GC cycles complete") print("\n[9] Waiting for delayed crash...") for i in range(3): time.sleep(1) print(f" {i+1}s...") gc.collect() print("\n" + "=" * 60) print("TEST PASSED - No crash detected") print("=" * 60) # Now test with jpegio if available if HAS_JPEGIO: print("\n" + "=" * 60) print("JPEGIO SPECIFIC TEST") print("=" * 60) import tempfile import os # Reload image data with open(image_path, 'rb') as f: carrier_data = f.read() print("\n[J1] Checking if image is JPEG...") img = Image.open(io.BytesIO(carrier_data)) is_jpeg = img.format == 'JPEG' img.close() print(f" Is JPEG: {is_jpeg}") if is_jpeg: print("\n[J2] Writing to temp file...") fd, temp_path = tempfile.mkstemp(suffix='.jpg') os.write(fd, carrier_data) os.close(fd) print(f" Temp file: {temp_path}") print("\n[J3] Reading with jpegio...") try: jpeg = jio.read(temp_path) print(f" jpegio.read() OK") print("\n[J4] Accessing coefficient arrays...") coef = jpeg.coef_arrays[0] print(f" Coef shape: {coef.shape}, dtype: {coef.dtype}") print("\n[J5] Counting usable positions...") positions = [] h, w = coef.shape for row in range(h): for col in range(w): if (row % 8 == 0) and (col % 8 == 0): continue if abs(coef[row, col]) >= 2: positions.append((row, col)) print(f" Usable positions: {len(positions)}") print("\n[J6] Cleaning up jpegio object...") del coef del jpeg gc.collect() print(" Deleted jpeg object") print("\n[J7] Removing temp file...") os.unlink(temp_path) print(" Temp file removed") gc.collect() print("\n[J8] Final GC...") except Exception as e: print(f" ERROR: {e}") import traceback traceback.print_exc() print("\n[J9] Waiting for delayed crash...") for i in range(3): time.sleep(1) print(f" {i+1}s...") gc.collect() print("\n" + "=" * 60) print("JPEGIO TEST PASSED - No crash detected") print("=" * 60) else: print(" Skipping jpegio test (not a JPEG)") print("\n\nAll tests completed successfully!")