Skip to content

Commit c2e3995

Browse files
committed
Improve Codename One UI test bundle discovery
1 parent fe905c2 commit c2e3995

File tree

1 file changed

+77
-18
lines changed

1 file changed

+77
-18
lines changed

scripts/ios/tests/HelloCodenameOneUITests.swift.tmpl

Lines changed: 77 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import XCTest
22
import UIKit
33
import CoreGraphics
44
import Darwin
5+
import Foundation
56

67
final class HelloCodenameOneUITests: XCTestCase {
78
private var app: XCUIApplication!
@@ -275,13 +276,10 @@ final class HelloCodenameOneUITests: XCTestCase {
275276
if let explicit = targetBundleIdentifier, !explicit.isEmpty {
276277
return explicit
277278
}
278-
do {
279-
let value = try app.value(forKey: "bundleID")
280-
if let actual = value as? String, !actual.isEmpty {
281-
return actual
279+
if let bundle: String = dynamicAppValue("bundleID") {
280+
if !bundle.isEmpty {
281+
return bundle
282282
}
283-
} catch {
284-
print("CN1SS:WARN:ui_test_bundle_resolution_failed error=\(error)")
285283
}
286284
return nil
287285
}
@@ -449,20 +447,16 @@ private final class CodenameOneMainInvoker {
449447
}
450448

451449
private func locateAppContainer(app: XCUIApplication, bundleIdentifier: String) -> String? {
452-
do {
453-
if let bundleURL = try app.value(forKey: "bundleURL") as? URL {
454-
return bundleURL.path
455-
}
456-
} catch {
457-
print("CN1SS:WARN:codenameone_main_kvc_failed key=bundleURL bundle=\(bundleIdentifier) error=\(error)")
450+
if let bundleURL: URL = dynamicAppValue("bundleURL"), !bundleURL.path.isEmpty {
451+
return bundleURL.path
458452
}
459453

460-
do {
461-
if let bundlePath = try app.value(forKey: "bundlePath") as? String {
462-
return bundlePath
463-
}
464-
} catch {
465-
print("CN1SS:WARN:codenameone_main_kvc_failed key=bundlePath bundle=\(bundleIdentifier) error=\(error)")
454+
if let bundlePath: String = dynamicAppValue("bundlePath"), !bundlePath.isEmpty {
455+
return bundlePath
456+
}
457+
458+
if let container = locateViaSimctl(bundleIdentifier: bundleIdentifier) {
459+
return container
466460
}
467461

468462
if let fallback = Bundle.main.infoDictionary?["CFBundleExecutable"] as? String {
@@ -486,6 +480,71 @@ private final class CodenameOneMainInvoker {
486480
return executable
487481
}
488482

483+
private func locateViaSimctl(bundleIdentifier: String) -> String? {
484+
let env = ProcessInfo.processInfo.environment
485+
guard let udid = env["SIMULATOR_UDID"], !udid.isEmpty else {
486+
return nil
487+
}
488+
489+
let task = Process()
490+
task.executableURL = URL(fileURLWithPath: "/usr/bin/xcrun")
491+
task.arguments = ["simctl", "get_app_container", udid, bundleIdentifier]
492+
493+
let stdoutPipe = Pipe()
494+
let stderrPipe = Pipe()
495+
task.standardOutput = stdoutPipe
496+
task.standardError = stderrPipe
497+
498+
do {
499+
try task.run()
500+
} catch {
501+
print("CN1SS:WARN:codenameone_main_simctl_failed bundle=\(bundleIdentifier) error=\(error)")
502+
return nil
503+
}
504+
505+
task.waitUntilExit()
506+
if task.terminationStatus != 0 {
507+
let data = stderrPipe.fileHandleForReading.readDataToEndOfFile()
508+
if let message = String(data: data, encoding: .utf8)?.trimmingCharacters(in: .whitespacesAndNewlines), !message.isEmpty {
509+
print("CN1SS:WARN:codenameone_main_simctl_failed bundle=\(bundleIdentifier) status=\(task.terminationStatus) stderr=\(message)")
510+
} else {
511+
print("CN1SS:WARN:codenameone_main_simctl_failed bundle=\(bundleIdentifier) status=\(task.terminationStatus)")
512+
}
513+
return nil
514+
}
515+
516+
let data = stdoutPipe.fileHandleForReading.readDataToEndOfFile()
517+
guard let output = String(data: data, encoding: .utf8)?.trimmingCharacters(in: .whitespacesAndNewlines), !output.isEmpty else {
518+
print("CN1SS:WARN:codenameone_main_simctl_empty bundle=\(bundleIdentifier)")
519+
return nil
520+
}
521+
522+
return output
523+
}
524+
525+
private func dynamicAppValue<T>(_ selectorName: String) -> T? {
526+
let selector = NSSelectorFromString(selectorName)
527+
guard app.responds(to: selector) else {
528+
return nil
529+
}
530+
guard let unmanaged = app.perform(selector) else {
531+
return nil
532+
}
533+
let value = unmanaged.takeUnretainedValue()
534+
switch value {
535+
case let typed as T:
536+
return typed
537+
case let number as NSNumber where T.self == Bool.self:
538+
return (number.boolValue as? T)
539+
case let string as NSString where T.self == String.self:
540+
return (string as String) as? T
541+
case let url as NSURL where T.self == URL.self:
542+
return (url as URL) as? T
543+
default:
544+
return nil
545+
}
546+
}
547+
489548
private struct InvocationContext {
490549
typealias InitConstantPoolFn = @convention(c) () -> Void
491550
typealias GetThreadLocalDataFn = @convention(c) () -> UnsafeMutableRawPointer?

0 commit comments

Comments
 (0)