Skip to content

Commit f659388

Browse files
committed
Fix for iOS simulator
1 parent c6abc3d commit f659388

1 file changed

Lines changed: 97 additions & 33 deletions

File tree

scripts/gha/test_simulator.py

Lines changed: 97 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@ def main(argv):
240240
device_name = FLAGS.ios_name
241241
device_os = FLAGS.ios_version
242242

243-
device_id = _create_and_boot_simulator("iOS", device_name, device_os)
243+
device_id, device_name, device_os = _create_and_boot_simulator("iOS", device_name, device_os)
244244
if not device_id:
245245
logging.error("simulator created fail")
246246
return 21
@@ -274,7 +274,7 @@ def main(argv):
274274
device_name = FLAGS.tvos_name
275275
device_os = FLAGS.tvos_version
276276

277-
device_id = _create_and_boot_simulator("tvOS", device_name, device_os)
277+
device_id, device_name, device_os = _create_and_boot_simulator("tvOS", device_name, device_os)
278278
if not device_id:
279279
logging.error("simulator created fail")
280280
return 21
@@ -500,49 +500,113 @@ def _shutdown_simulator():
500500

501501

502502
def _create_and_boot_simulator(apple_platform, device_name, device_os):
503-
"""Create a simulator locally. Will wait until this simulator booted."""
504-
_shutdown_simulator()
505-
command = "xcrun xctrace list devices 2>&1 | grep \"%s Simulator (%s)\" | awk -F'[()]' '{print $4}'" % (device_name, device_os)
506-
logging.info("Get test simulator: %s", command)
507-
result = subprocess.Popen(command, universal_newlines=True, shell=True, stdout=subprocess.PIPE)
508-
device_id = result.stdout.readline().strip()
503+
"""Create a simulator locally. Will wait until this simulator booted.
509504
510-
if not device_id:
511-
# download and create device
512-
args = ["brew", "install", "xcodesorg/made/xcodes"]
513-
logging.info("Download xcodes: %s", " ".join(args))
514-
subprocess.run(args=args, check=True)
505+
Returns a tuple of (device_id, device_name, device_os).
506+
"""
507+
_shutdown_simulator()
515508

516-
# Get the set of available versions for the given Apple platform
517-
args = ["xcodes", "runtimes"]
518-
runtimes = subprocess.run(args=args, capture_output=True, text=True, check=True)
519-
available_versions = re.findall('{0} ([\d|.]+)'.format(apple_platform), runtimes.stdout.strip())
520-
logging.info("Found available versions for %s: %s", apple_platform, ", ".join(available_versions))
509+
# Find available runtimes using simctl
510+
try:
511+
runtimes_result = subprocess.run(
512+
["xcrun", "simctl", "list", "runtimes", "-j"],
513+
capture_output=True, text=True, check=True)
514+
runtimes_data = json.loads(runtimes_result.stdout)
515+
except Exception:
516+
logging.exception("Failed to get runtimes list from simctl")
517+
return None, None, None
518+
519+
# Filter runtimes by platform and availability
520+
available_runtimes = []
521+
for r in runtimes_data.get("runtimes", []):
522+
if r.get("isAvailable") and r.get("platform") == apple_platform:
523+
available_runtimes.append(r)
524+
525+
if not available_runtimes:
526+
logging.error("No available runtimes found for platform: %s", apple_platform)
527+
return None, None, None
528+
529+
# Find matching runtime version
530+
target_runtime = None
531+
for r in available_runtimes:
532+
version = r.get("version")
533+
if version == device_os or version.startswith(device_os):
534+
target_runtime = r
535+
break
521536

522-
# If the requested version is available, use it, otherwise default to the latest
523-
if (device_os not in available_versions):
524-
logging.warning("Unable to find version %s, will fall back to %s", device_os, available_versions[-1])
525-
if FLAGS.ci:
526-
print("::warning ::Unable to find %s version %s, will fall back to %s" % (apple_platform, device_os, available_versions[-1]))
527-
device_os = available_versions[-1]
537+
if not target_runtime:
538+
# Fall back to the latest runtime version
539+
def parse_version(version_str):
540+
sanitized = re.sub(r'[^\d.]', '', version_str)
541+
parts = []
542+
for x in sanitized.split('.'):
543+
if x.isdigit():
544+
parts.append(int(x))
545+
return parts
546+
547+
available_runtimes.sort(key=lambda r: parse_version(r.get("version")))
548+
target_runtime = available_runtimes[-1]
549+
fallback_os = target_runtime.get("version")
550+
logging.warning("Requested %s version %s is not available. Falling back to %s",
551+
apple_platform, device_os, fallback_os)
552+
if FLAGS.ci:
553+
print("::warning ::Requested %s version %s is not available, falling back to %s" %
554+
(apple_platform, device_os, fallback_os))
555+
device_os = fallback_os
556+
557+
runtime_id = target_runtime.get("identifier")
558+
logging.info("Using runtime: %s (ID: %s)", target_runtime.get("name"), runtime_id)
559+
560+
# Check if a matching simulator device already exists
561+
try:
562+
devices_result = subprocess.run(
563+
["xcrun", "simctl", "list", "devices", "-j"],
564+
capture_output=True, text=True, check=True)
565+
devices_data = json.loads(devices_result.stdout)
566+
except Exception:
567+
logging.exception("Failed to get devices list from simctl")
568+
return None, None, None
569+
570+
devices_under_runtime = devices_data.get("devices", {}).get(runtime_id, [])
571+
device_id = None
572+
for d in devices_under_runtime:
573+
if d.get("isAvailable") and d.get("name") == device_name:
574+
device_id = d.get("udid")
575+
logging.info("Found existing simulator device: %s (%s)", device_name, device_id)
576+
break
528577

529-
args = ["sudo", "xcodes", "runtimes", "install", "%s %s" % (apple_platform, device_os)]
530-
logging.info("Download simulator: %s", " ".join(args))
531-
subprocess.run(args=args, check=False)
578+
if not device_id:
579+
# Create new device if not found
580+
args = ["xcrun", "simctl", "create", "test_simulator", device_name, runtime_id]
581+
logging.info("Creating test simulator: %s", " ".join(args))
582+
try:
583+
result = subprocess.run(args=args, capture_output=True, text=True, check=True)
584+
device_id = result.stdout.strip()
585+
logging.info("Created simulator: %s", device_id)
586+
except subprocess.CalledProcessError as e:
587+
logging.error("Failed to create simulator: %s", e.stderr)
588+
# Fall back to the first available device under this runtime
589+
for d in devices_under_runtime:
590+
if d.get("isAvailable"):
591+
device_id = d.get("udid")
592+
device_name = d.get("name")
593+
logging.info("Fallback to first available device: %s (%s)", device_name, device_id)
594+
break
532595

533-
args = ["xcrun", "simctl", "create", "test_simulator", device_name, "%s%s" % (apple_platform, device_os)]
534-
logging.info("Create test simulator: %s", " ".join(args))
535-
result = subprocess.run(args=args, capture_output=True, text=True, check=True)
536-
device_id = result.stdout.strip()
596+
if not device_id:
597+
logging.error("No simulator device available or created")
598+
return None, None, None
537599

600+
# Boot the simulator
538601
args = ["xcrun", "simctl", "boot", device_id]
539-
logging.info("Boot my simulator: %s", " ".join(args))
602+
logging.info("Boot simulator: %s", " ".join(args))
540603
subprocess.run(args=args, check=True)
541604

542605
args = ["xcrun", "simctl", "bootstatus", device_id]
543606
logging.info("Wait for simulator to boot: %s", " ".join(args))
544607
subprocess.run(args=args, check=True)
545-
return device_id
608+
609+
return device_id, device_name, device_os
546610

547611

548612
def _delete_simulator(device_id):

0 commit comments

Comments
 (0)