Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix mac os sandbox check slowness #3879

Merged
merged 4 commits into from
Aug 23, 2024
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
17 changes: 13 additions & 4 deletions Sources/Misc/SandboxEnvironmentDetector.swift
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,14 @@ final class BundleSandboxEnvironmentDetector: SandboxEnvironmentDetector {
}

#if os(macOS) || targetEnvironment(macCatalyst)
return !self.isProductionReceipt || !self.isMacAppStore
// this relies on an undocumented field in the receipt that provides the Environment.
// if it's not present, we go to a secondary check.
if let isProductionReceipt = self.isProductionReceipt {
return !isProductionReceipt
} else {
return !self.isMacAppStore
}

#else
return path.contains("sandboxReceipt")
#endif
Expand All @@ -73,12 +80,14 @@ extension BundleSandboxEnvironmentDetector: Sendable {}

private extension BundleSandboxEnvironmentDetector {

var isProductionReceipt: Bool {
var isProductionReceipt: Bool? {
do {
return try self.receiptFetcher.fetchAndParseLocalReceipt().environment == .production
let receiptEnvironment = try self.receiptFetcher.fetchAndParseLocalReceipt().environment
guard receiptEnvironment != .unknown else { return nil } // don't make assumptions if we're not sure
return receiptEnvironment == .production
} catch {
Logger.error(Strings.receipt.parse_receipt_locally_error(error: error))
return false
return nil
}
}

Expand Down
110 changes: 80 additions & 30 deletions Tests/UnitTests/Misc/SandboxEnvironmentDetectorTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ class SandboxEnvironmentDetectorTests: TestCase {
// `macOS` sandbox detection does not rely on receipt path
class SandboxEnvironmentDetectorTests: TestCase {

func testIsNotSandboxIfReceiptIsProductionAndMAS() throws {
func testIsNotSandboxIfReceiptIsProduction() throws {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we no longer look at MAS if we know what the receipt environment is

expect(
SystemInfo.with(
macAppStore: true,
Expand All @@ -54,41 +54,85 @@ class SandboxEnvironmentDetectorTests: TestCase {
) == false
}

func testIsSandboxIfReceiptIsProductionAndNotMAS() throws {
func testIsSandboxIfReceiptIsNotProduction() throws {
expect(
SystemInfo.with(
macAppStore: false,
receiptEnvironment: .production
receiptEnvironment: .sandbox
).isSandbox
) == true
}

func testIsSandboxIfReceiptIsNotProductionAndNotMAS() throws {
expect(
SystemInfo.with(
macAppStore: false,
receiptEnvironment: .sandbox
).isSandbox
) == true
Comment on lines -66 to -72
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this test and the next one are technically still there, but now in a more complete test that also verifies that we indeed use MAS when needed and skip it when not needed

func testIsSandboxWhenReceiptEnvironmentIsUnknownDefaultToMacAppStoreDetector() throws {
var isSandbox = false
var macAppStoreDetector = MockMacAppStoreDetector(isMacAppStore: !isSandbox)
var detector = SystemInfo.with(
macAppStore: !isSandbox,
receiptEnvironment: .unknown,
macAppStoreDetector: macAppStoreDetector
)

expect(detector.isSandbox) == isSandbox
expect(macAppStoreDetector.isMacAppStoreCalled) == true

isSandbox = !isSandbox

macAppStoreDetector = MockMacAppStoreDetector(isMacAppStore: !isSandbox)
detector = SystemInfo.with(
macAppStore: !isSandbox,
receiptEnvironment: .unknown,
macAppStoreDetector: macAppStoreDetector
)

expect(detector.isSandbox) == isSandbox
}

func testIsSandboxIfReceiptIsNotProductionAndMAS() throws {
expect(
SystemInfo.with(
macAppStore: true,
receiptEnvironment: .sandbox
).isSandbox
) == true
func testIsSandboxWhenReceiptParsingFailsDefaultsToMacAppStoreDetector() throws {
var isSandbox = false
var macAppStoreDetector = MockMacAppStoreDetector(isMacAppStore: !isSandbox)
var detector = SystemInfo.with(
macAppStore: !isSandbox,
failReceiptParsing: true,
macAppStoreDetector: macAppStoreDetector
)

expect(detector.isSandbox) == isSandbox
expect(macAppStoreDetector.isMacAppStoreCalled) == true

isSandbox = !isSandbox

macAppStoreDetector = MockMacAppStoreDetector(isMacAppStore: !isSandbox)
detector = SystemInfo.with(
macAppStore: !isSandbox,
failReceiptParsing: true,
macAppStoreDetector: macAppStoreDetector
)

expect(detector.isSandbox) == isSandbox
}

func testIsSandboxIfReceiptParsingFailsAndBundleSignatureIsNotMAS() throws {
expect(
SystemInfo.with(
macAppStore: false,
receiptEnvironment: .production,
failReceiptParsing: true
).isSandbox
) == true
func testIsSandboxWhenReceiptIsProductionReturnsProductionAndDoesntHitMacAppStoreDetector() throws {
let macAppStoreDetector = MockMacAppStoreDetector(isMacAppStore: false)
let detector = SystemInfo.with(
macAppStore: false,
receiptEnvironment: .production,
macAppStoreDetector: macAppStoreDetector
)

expect(detector.isSandbox) == false
expect(macAppStoreDetector.isMacAppStoreCalled) == false
}

func testIsSandboxWhenReceiptIsSandboxReturnsSandboxAndDoesntHitMacAppStoreDetector() throws {
let macAppStoreDetector = MockMacAppStoreDetector(isMacAppStore: false)
let detector = SystemInfo.with(
macAppStore: false,
receiptEnvironment: .sandbox,
macAppStoreDetector: macAppStoreDetector
)

expect(detector.isSandbox) == true
expect(macAppStoreDetector.isMacAppStoreCalled) == false
}

}
Expand All @@ -104,7 +148,8 @@ private extension SandboxEnvironmentDetector {
inSimulator: Bool = false,
macAppStore: Bool = false,
receiptEnvironment: AppleReceipt.Environment = .production,
failReceiptParsing: Bool = false
failReceiptParsing: Bool = false,
macAppStoreDetector: MockMacAppStoreDetector? = nil
) -> SandboxEnvironmentDetector {
let bundle = MockBundle()
bundle.receiptURLResult = result
Expand All @@ -126,7 +171,7 @@ private extension SandboxEnvironmentDetector {
isRunningInSimulator: inSimulator,
receiptFetcher: MockLocalReceiptFetcher(mockReceipt: mockReceipt,
failReceiptParsing: failReceiptParsing),
macAppStoreDetector: MockMacAppStoreDetector(isMacAppStore: macAppStore)
macAppStoreDetector: macAppStoreDetector ?? MockMacAppStoreDetector(isMacAppStore: macAppStore)
)
}

Expand All @@ -151,12 +196,17 @@ private final class MockLocalReceiptFetcher: LocalReceiptFetcherType {

}

private struct MockMacAppStoreDetector: MacAppStoreDetector {
private final class MockMacAppStoreDetector: MacAppStoreDetector, @unchecked Sendable {

let isMacAppStore: Bool
let isMacAppStoreValue: Bool
private(set) var isMacAppStoreCalled = false

init(isMacAppStore: Bool) {
self.isMacAppStore = isMacAppStore
self.isMacAppStoreValue = isMacAppStore
}

var isMacAppStore: Bool {
isMacAppStoreCalled = true
return isMacAppStoreValue
}
Comment on lines -154 to +211
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changes here are to make it possible to check whether it was called

}