Files
SNANDer-Droid-MAX/utils/test_stability.py
T

168 lines
6.0 KiB
Python
Executable File

import subprocess
import os
import time
import random
import sys
SNANDER_PATH = "./releases/darwin-arm64/snander"
TEST_FILE = "test_data.bin"
READ_FILE = "read_data.bin"
FULL_SIZE = 16 * 1024 * 1024
BLOCK_SIZE = 64 * 1024 # 64KB
def generate_random_file(filename, size):
with open(filename, 'wb') as f:
f.write(os.urandom(size))
def run_snander(args, timeout=300):
start_time = time.time()
try:
print(f" Running: snander {' '.join(args)}")
result = subprocess.run(
[SNANDER_PATH] + args,
capture_output=True,
text=True,
timeout=timeout
)
end_time = time.time()
output = result.stdout + result.stderr
is_success = False
if "Status: OK" in output:
is_success = True
elif "-i" in args and "Detected SPI NOR Flash" in output:
is_success = True
elif "-r" in args and not "error" in output.lower(): # Read sometimes doesn't say Status: OK explicitly if piping?
# But we are using -r filename.
pass
if "Status: BAD" in output or "failed" in output.lower() or "error" in output.lower():
if "libusb" not in output.lower(): # Ignore libusb spam if possible, but "error" might key on LIBUSB_ERROR
pass
# snander often prints LIBUSB control errors that are non-fatal.
# We rely on specific success cues or lack of specific failure cues?
# Best check: "Status: BAD" is definite fail.
return True, end_time - start_time, output # Return True by default and let caller parse output for critical errors if needed, or refine success logic.
except subprocess.TimeoutExpired:
return False, timeout, "Timeout"
except Exception as e:
return False, 0, str(e)
def test_identification(iterations=5):
print(f"--- Testing Identification ({iterations} times) ---")
success_count = 0
for i in range(iterations):
success, duration, output = run_snander(["-i"])
if success:
print(f"[{i+1}/{iterations}] ID OK ({duration:.2f}s)")
success_count += 1
else:
print(f"[{i+1}/{iterations}] ID FAILED")
# print(output) # Optional: print only if debugging
time.sleep(1)
return success_count == iterations
def run_stability_test(iterations=10, chunk_size=1024*1024):
print(f"--- Starting Stability Test Loop ({iterations} iterations, {chunk_size/1024:.0f}KB chunks) ---")
stats = {
"erase_time": [],
"write_speed": [], # KB/s
"read_speed": [], # KB/s
"verify_errors": 0,
"usb_errors": 0
}
# 16MB chip capacity
max_addr = 16 * 1024 * 1024 - chunk_size
for i in range(iterations):
# Pick a random 64KB-aligned address
addr = random.randrange(0, max_addr, 64 * 1024)
print(f"\n[Iteration {i+1}/{iterations}] Address: {hex(addr)}")
try:
# 1. Erase
print(" Erasing...")
success, dur, out = run_snander(["-e", "-a", str(addr), "-l", str(chunk_size)])
if not success or "Status: OK" not in out:
print(" Erase FAILED.")
print(out)
stats["usb_errors"] += 1
continue
stats["erase_time"].append(dur)
print(f" Erase OK ({dur:.2f}s)")
# 2. Write
print(" Writing...")
generate_random_file(TEST_FILE, chunk_size)
success, dur, out = run_snander(["-w", TEST_FILE, "-a", str(addr)])
if not success or "Status: OK" not in out:
print(" Write FAILED.")
stats["usb_errors"] += 1
continue
speed = (chunk_size / 1024) / dur
stats["write_speed"].append(speed)
print(f" Write OK ({dur:.2f}s, {speed:.2f} KB/s)")
# 3. Read & Verify
print(" Verifying...")
if os.path.exists(READ_FILE): os.remove(READ_FILE)
success, dur, out = run_snander(["-r", READ_FILE, "-l", str(chunk_size), "-a", str(addr)])
if not success or not os.path.exists(READ_FILE):
print(" Read FAILED.")
stats["usb_errors"] += 1
continue
read_speed = (chunk_size / 1024) / dur
stats["read_speed"].append(read_speed)
# Binary Comparison
with open(TEST_FILE, 'rb') as f1, open(READ_FILE, 'rb') as f2:
if f1.read() == f2.read():
print(f" Verify OK ({read_speed:.2f} KB/s)")
else:
print(f" Verify FAILED (Content Mismatch)")
stats["verify_errors"] += 1
except Exception as e:
print(f" Exception: {e}")
stats["usb_errors"] += 1
time.sleep(1) # Backoff
# Report
print("\nXXX RESULTS XXX")
print(f"Total Iterations: {iterations}")
print(f"Successful Writes: {len(stats['write_speed'])}")
print(f"Verify Errors: {stats['verify_errors']}")
print(f"USB/Command Errors: {stats['usb_errors']}")
if stats['write_speed']:
avg_w = sum(stats['write_speed']) / len(stats['write_speed'])
max_w = max(stats['write_speed'])
min_w = min(stats['write_speed'])
print(f"Write Speed: Avg {avg_w:.2f} KB/s (Min {min_w:.2f}, Max {max_w:.2f})")
if stats['read_speed']:
avg_r = sum(stats['read_speed']) / len(stats['read_speed'])
print(f"Read Speed: Avg {avg_r:.2f} KB/s")
def main():
if not os.path.exists(SNANDER_PATH):
print("Binary not found")
return
# Identification Check
test_identification(3)
# Run Stability Loop (10 x 1MB)
run_stability_test(10, 1024 * 1024)
# If users wants full chip test specifically, they can ask or we can add a flag.
# But 1MB chunks give better granular feedback.
if __name__ == "__main__":
main()