Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ import AppKit

@MainActor
final class AppDelegate: NSObject, NSApplicationDelegate {
func applicationWillFinishLaunching(_ notification: Notification) {
NSWindow.allowsAutomaticWindowTabbing = false
}

func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
true
}
Expand Down
2 changes: 1 addition & 1 deletion src/SimStoreApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ struct SimStoreApp: App {
@NSApplicationDelegateAdaptor(AppDelegate.self) private var appDelegate

var body: some Scene {
WindowGroup("SimStore") {
Window("SimStore", id: "main") {
ContentView()
}
.defaultSize(width: 1180, height: 780)
Expand Down
41 changes: 18 additions & 23 deletions tests/AppSmokeTest.swift
Original file line number Diff line number Diff line change
@@ -1,48 +1,43 @@
import AppKit
import CoreGraphics
import Foundation

@main
struct AppSmokeTest {
static func main() {
@MainActor
static func main() async {
do {
try verifyWindow()
try await verifyWindow()
} catch {
FileHandle.standardError.write(
Data("App smoke test failed: \(error.localizedDescription)\n".utf8))
exit(EXIT_FAILURE)
}
}

static func verifyWindow() throws {
@MainActor
static func verifyWindow() async throws {
guard CommandLine.arguments.count == 2 else {
throw failure("Expected the App Bundle path.")
}
let bundleURL = URL(fileURLWithPath: CommandLine.arguments[1], isDirectory: true)
guard let executable = Bundle(url: bundleURL)?.executableURL else {
throw failure("The App Bundle has no executable.")
}
let process = Process()
process.executableURL = executable
try process.run()
defer {
if process.isRunning {
process.terminate()
}
process.waitUntilExit()
}
let configuration = NSWorkspace.OpenConfiguration()
configuration.createsNewApplicationInstance = true
let application = try await NSWorkspace.shared.openApplication(
at: bundleURL, configuration: configuration)
defer { application.terminate() }

let deadline = Date().addingTimeInterval(10)
var foundWindow = false
while Date() < deadline {
guard process.isRunning else {
guard !application.isTerminated else {
throw failure(
"The app exited before its window appeared (status \(process.terminationStatus)).")
"The app exited before its window appeared.")
}
let windows =
CGWindowListCopyWindowInfo([.optionOnScreenOnly, .excludeDesktopElements], kCGNullWindowID)
as? [[String: Any]] ?? []
foundWindow = windows.contains { window in
guard (window[kCGWindowOwnerPID as String] as? Int32) == process.processIdentifier,
guard (window[kCGWindowOwnerPID as String] as? Int32) == application.processIdentifier,
(window[kCGWindowLayer as String] as? Int) == 0,
let bounds = window[kCGWindowBounds as String] as? [String: Any],
let width = bounds["Width"] as? Double,
Expand All @@ -55,18 +50,18 @@ struct AppSmokeTest {
if foundWindow {
break
}
Thread.sleep(forTimeInterval: 0.1)
try await Task.sleep(for: .milliseconds(100))
}
guard foundWindow else {
throw failure(
"No visible app window appeared. Run this test from a logged-in macOS GUI session.")
}
Thread.sleep(forTimeInterval: 2)
guard process.isRunning else {
try await Task.sleep(for: .seconds(2))
guard !application.isTerminated else {
throw failure("The app exited after showing its window.")
}
print(
"PASS: SimStore launched with a visible window and remained alive (PID \(process.processIdentifier))."
"PASS: SimStore launched with a visible window and remained alive (PID \(application.processIdentifier))."
)
}

Expand Down