autool/utils_ios/fastbot_runner.py
2026-06-17 19:44:18 +08:00

147 lines
5.0 KiB
Python

#!/usr/bin/env python3
"""
Fastbot Runner for iOS
Runs Fastbot testing and monitors status via HTTP API.
"""
import time
import logging
import subprocess
from typing import Optional
import sys
import os
from pathlib import Path
# Setup path
ROOT = Path(__file__).resolve().parent
sys.path.insert(0, str(ROOT))
logger = logging.getLogger(__name__)
class FastbotTestRunner:
"""Fastbot test runner with status monitoring"""
def __init__(self, udid: str, fastbot_port: int = 9197):
self.udid = udid
self.fastbot_port = fastbot_port
def monitor_status(self, interval: int = 5, process: Optional[subprocess.Popen] = None) -> None:
"""
Monitor Fastbot running status via HTTP API.
:param interval: Status check interval in seconds
:param process: Fastbot process to monitor
"""
try:
import requests
except ImportError:
logger.warning("requests not available, skipping status monitoring")
if process:
process.wait()
return
url = f"http://127.0.0.1:{self.fastbot_port}/status"
logger.info(f"Monitoring Fastbot status at {url}")
last_status = None
while True:
# Check if process has ended
if process and process.poll() is not None:
logger.info("Fastbot process has ended")
break
try:
response = requests.get(url, timeout=2)
if response.status_code == 200:
data = response.json()
running = data.get("runingStatus", False) # Note: typo in API
# Log status changes
if running != last_status:
if running:
logger.info("[STATUS] Fastbot is running ✓")
else:
logger.info("[STATUS] Fastbot has stopped")
break
last_status = running
else:
logger.warning(f"[STATUS] HTTP {response.status_code}")
except requests.RequestException as e:
if last_status is not None: # Only warn if we were connected before
logger.warning(f"[STATUS] Cannot reach status endpoint: {e}")
except Exception as e:
logger.error(f"[STATUS] Unexpected error: {e}")
time.sleep(interval)
logger.info("Status monitoring completed")
def run_fastbot(
self,
target_bundle_id: str,
duration: int = 600,
throttle: int = 1000,
output_dir: Optional[str] = None
) -> tuple:
"""
Run Fastbot test with status monitoring.
:param target_bundle_id: App to test
:param duration: Test duration in seconds
:param throttle: Event throttle in milliseconds
:param output_dir: Output directory for logs
:return: (exit_code, stdout)
"""
from utils_ios.go_ios.go_ios_runner import GoIOSRunner
logger.info(f"Starting Fastbot test for {target_bundle_id}")
logger.info(f"Duration: {duration}s, Throttle: {throttle}ms")
runner = GoIOSRunner(udid=self.udid)
try:
# Start infrastructure (tunnel not needed, but port forward required)
logger.info("Starting infrastructure...")
if not runner.start_infrastructure(wda_port=8100, fastbot_port=self.fastbot_port):
logger.error("Failed to start infrastructure")
return 1, ""
logger.info("Infrastructure started, waiting for stabilization...")
time.sleep(3)
# Start Fastbot
logger.info("Starting Fastbot...")
fastbot_process = runner.start_fastbot(
target_bundle_id=target_bundle_id,
duration=duration,
throttle=throttle
)
logger.info(f"Fastbot started with PID: {fastbot_process.pid}")
# Monitor status
self.monitor_status(interval=5, process=fastbot_process)
# Wait for completion
fastbot_process.wait()
returncode = fastbot_process.returncode
logger.info(f"Fastbot completed with exit code: {returncode}")
return returncode, ""
except KeyboardInterrupt:
logger.info("User interrupted Fastbot test")
return 130, "" # SIGINT exit code
finally:
logger.info("Cleaning up...")
runner.stop_all()
if __name__ == "__main__":
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
# Quick test
runner = FastbotTestRunner(udid="00008101-0016601022C0001E")
runner.run_fastbot("com.apple.AppStore", duration=30, throttle=1000)