mirror of
https://github.com/Droid-MAX/SNANDer
synced 2026-07-08 07:32:02 +00:00
168 lines
6.0 KiB
Python
Executable File
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()
|