Operation not permitted - MacOS - Swift - UITests

I`m trying create simple http sever, when I do it in main target of my macOS app then it works, but when I do it in uitests target it fails (result == -1 (Operation not permitted)). Interestingly when I run it in iOS uiTests target then it works.

let sockfd = socket(AF_INET, SOCK_STREAM, 0)
    var serverAddress = sockaddr_in(sin_len: __uint8_t(MemoryLayout<sockaddr_in>.size),
                                    sin_family: sa_family_t(AF_INET),
                                    sin_port: CFSwapInt16HostToBig(8080),
                                    sin_addr: in_addr(s_addr: inet_addr("127.0.0.1")),
                                    sin_zero: (0, 0, 0, 0, 0, 0, 0, 0))

    let result = withUnsafePointer(to: &serverAddress) {
        $0.withMemoryRebound(to: sockaddr.self, capacity: 1) {
          Darwin.bind(sockfd, $0, socklen_t(MemoryLayout<sockaddr_in>.size))
        }
    }

I've got set in my main target entitlements:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>com.apple.security.app-sandbox</key>
	<true/>
	<key>com.apple.security.network.client</key>
	<true/>
	<key>com.apple.security.network.server</key>
	<true/>
</dict>
</plist>

What can be reason that it fails? how can I fix it?

The error you're encountering when trying to create a simple HTTP server in your macOS UI tests target, specifically "result == -1 (Operation not permitted)," is likely due to macOS sandboxing and permissions restrictions. Unlike the main app target, UI tests in macOS have a more restricted environment, which can affect your ability to perform certain operations, such as creating sockets and binding to ports.

To create a server socket and bind to a port in a UI tests target, you may need to modify your app's entitlements and ensure that your app has the necessary permissions. Here are some steps to consider:

  1. Add Network Entitlements: Make sure that your UI tests target also has the necessary network entitlements. You can add these entitlements to your UI tests target by including an entitlements file specifically for the UI tests target.

    Create a new entitlements file (e.g., "UITestEntitlements.plist") that contains the network entitlements you provided in your question:

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
    <plist version="1.0">
    <dict>
        <key>com.apple.security.app-sandbox</key>
        <true/>
        <key>com.apple.security.network.client</key>
        <true/>
        <key>com.apple.security.network.server</key>
        <true/>
    </dict>
    </plist>
    

    Then, in your UI tests target's build settings, specify this entitlements file as the "Code Signing Entitlements" for the UI tests target.

  2. Use a Non-Reserved Port: macOS may have restrictions on binding to well-known ports (e.g., port 80). Try binding to a non-reserved port (e.g., a port number greater than 1024) to see if that resolves the issue.

  3. Handle Permissions Prompt: When your server code runs in your UI tests, it may trigger a permissions prompt for network access. Ensure that your UI tests are designed to handle any permissions prompts that may appear during execution.

  4. Check for UI Test Constraints: UI tests may run in a separate environment with different constraints compared to the main app target. Make sure that your server code is adapted to run within the context of UI tests. Consider any specific needs or configurations required for UI testing.

  5. Use Mocking: Instead of creating an actual server socket in your UI tests, you might consider using mock server responses or stubs for UI testing purposes. This can help avoid network-related issues in UI tests.

  6. Check for Code Signing: Ensure that your UI tests target is correctly signed with the appropriate provisioning profile.

  7. Check System Preferences: On macOS, you can check the "Security & Privacy" settings in the System Preferences to make sure your app and UI tests have the necessary permissions for network access.

By following these steps, you can improve the chances of creating a simple HTTP server within your UI tests target on macOS without encountering "Operation not permitted" errors.

See if Viewing Sandbox Violation Reports gets you any closer.

Also see what value is stored in errno, if sockfd has been returned containing -1. Something akin to:

print("Error from socket(): \(String(describing: strerror(errno)))")

I do dislike ChatGPT, having cleaned up source code "hallucinated" by that.

Interestingly when I run it in iOS uiTests target then it works.

iOS has a sandbox, but it’s not exactly the same sandbox as the App Sandbox on macOS. Notably, the iOS sandbox does not require an entitlement to access the network, which is why you’re not seeing the problem there.

when I do it in main target of my macOS app then it works, but when I do it in uitests target it fails

That’s because your UI tests are hosted in a test runner process, and that process is sandboxed and doesn’t have the com.apple.security.network.server entitlement.

IMO the UI test runner show allow incoming network connection. If you agree, feel free to file a bug explaining why it’s important to you. Please post your bug number, just for the record.

As to what you can do about this, well, I’d like to discuss the big picture here. Why do you need to run an HTTP server in your UI test runner?

ps Instead of CFSwapInt16HostToBig, try UInt16(8080).bigEndian.

pps As you’ve probably noticed, calling BSD Sockets from Swift is a royal pain. I have some helpers that I use for this; see Calling BSD Sockets from Swift. Note that these aren’t intended for production code, but are helpful in cases like this.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

iOS has a sandbox, but it’s not exactly the same sandbox as the App Sandbox on macOS. Notably, the iOS sandbox does not require an entitlement to access the network, which is why you’re not seeing the problem there.

It makes sense.

Why do you need to run an HTTP server in your UI test runner?

For instance, in iOS, I use a simple HTTP localhost server with which I can mock responses and status codes for endpoints used in the app. This approach is useful if I want to run quick tests that simply navigate through the app to verify if screens and views are displayed as expected, based on the provided data. This way, I can avoid relying on scripts that set up my backend app or using non-native frameworks written in Python.

pps As you’ve probably noticed, calling BSD Sockets from Swift is a royal pain. I have some helpers that I use for this; see Calling BSD Sockets from Swift. Note that these aren’t intended for production code, but are helpful in cases like this.

I'm gonna check!

I use a simple HTTP localhost server with which I can mock responses and status codes for endpoints used in the app.

Yeah, that’s pretty much what I expected.

How is your UI test telling your app to use this HTTP server rather than the back end?

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Operation not permitted - MacOS - Swift - UITests
 
 
Q