-
Notifications
You must be signed in to change notification settings - Fork 15
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
Add intial tests for XPCEncoder #6
Add intial tests for XPCEncoder #6
Conversation
@trilemma-dev what do you think? |
1030bd5
to
efe1279
Compare
Thanks for putting this together. Will review; may take me a few days to get through all of it. FYI I'm in the NZDT timezone. |
I read through the PR and this brought up several important points that are better discussed at a high level before getting into any specifics on the details of the code:
|
Some thoughts, in (kind of) reverse order.
That's an untested code path, so I'm afraid to make that change. If you make the change, I suppose you use whatever SecureXPC-using project that you have to test it :D
Yes, absolutely! :) I suppose I should have communicated my higher level intentions for the test suite. I think it should consist of:
Part of my motivation here was that I wanted to locally refactor to see if I can make the
I think this is easy enough to do that it might be worth doing. In particular, I've noticed that You can get a rough of idea of what that might look like, here: amomchilov/SecureXPC@add-intial-tests-for-XPCEncoder...Alex/new-scalar-support . Of course, I only changed the encoder half, and the decoder would need to match.
I don't have an immediate need for passing FDs or shared memory regions in my program yet, but my app does need to pass an This is the approach that's recommended in Apple's EvenBetterAuthorizationSample, so I think supporting |
I've made this change and committed it. It should now be trivial to directly test the encoder or decoder independent of the rest of the system. I'll reply to the rest of your comments tomorrow as it's a bit after 2AM here (got a bit carried away making this refactor)! |
Haha I sure know that feeling! Good night! |
efe1279
to
c64bbaa
Compare
Dang it, I regret force-pushing that. Oh well, doesn't matter, I got rid of the 🧹 super clean! |
0a4fda5
to
c7b9a2e
Compare
Thanks for providing the additional context. I'm very welcoming of contributions to this framework, just wanted to make sure there was a shared understanding of how and why things work in this space. In particular, I didn't want you to have a mistaken understanding that by committing this test of tests that the on wire protocol would remain stable (pre-1.0.0 that is).
Sounds good.
Makes sense.
I believe this is necessary in order to test that arbitrary
I haven't fully thought this through, so not actually sure if this will work but I think this ought to be feasible using a Launch Agent. They run as the user, not root, and so I'm hoping it's possible to package these up. Just to be extra explicit, this framework is for XPC Mach Service communication, which is not what XPC Services use (yeah it's stupidly confusing how Apple overloaded terms here).
At the very least, I want to add testing for routes. If there was ever a regression here it'd likely be very disruptive.
Intriguing. It's not obvious to me that's possible (at least in a useful way). Much of the encoding does not occur at the time a call is made to the encoder, but instead at the "end" of the process when
Oof, okay this is going to be rather complicated. Will need a bunch of thought. If you weren't already aware, the term "sandbox" used by EvenBetterAuthorizationSample does not refer to same thing as the sandboxing macOS uses today. The sample is just that old :) Will begin reviewing your PR again in light of the above. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! This looks like a really good test of the basics.
If your intention is to use this set of tests in order to make changes to XPCEncoder
then the remaining portion is encoding classes which need to encode their super classes. This along with nested unkeyed contains and keyed containers makes up a lot of the complexity to how XPCEncoder
is designed. That's because they require deferred encoding of the actual values because at the time they need to return their containers or encoders what will actually need to be encoded isn't known.
See XPCUnkeyedEncodingContainer
's superEncoder()
function and XPCKeyedEncodingContainer
's superEncoder()
and superEncoder(forKey key: K)
functions for the super class encoding.
/// - sourceArray: The values to wrap and pack into the XPC array | ||
/// - transformIntoXPCObject: The closures used to transform the source elements into XPC objects | ||
/// - Returns: an XPC array containing the transformed XPC objects | ||
func createXPCArray<T>(from sourceArray: [T], using transformIntoXPCObject: (T) -> xpc_object_t) -> xpc_object_t { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't believe this function is needed if the one below it which takes [T?] also exists?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a reason I did this, but I don't remember it, haha. I'll look into it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm going to dedupe all comments. When we decide on an outcome from the main thread, i'll follow up and fix them all
@@ -0,0 +1,117 @@ | |||
// | |||
// TestHelpers.swift |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This approach seems pretty convenient for containers of all the same types and no nesting (so arrays or dictionaries with all of the same scalar elements/values), but I could see it being rather cumbersome to use when testing deeply nested types or varying types. Considering there's a finite number of types that the Encoder
protocol (and its related protocols it "vends"), it may be more convenient to create an extension for each of those types and then have them encode themselves. (Although maybe this would be tricky for nil
?) That way mixed type arrays/dictionaries will just encode themselves and deeply nested types should be pretty trivial to support via mutual recursion of their elements/values (when applicable).
This feedback is not intended to mean I'd like you to change your approach for this PR, but something to consider in the future.
func testEncodes_Float_SignalingNaN_as_XPCDouble_QuietNaN() throws { | ||
let result = xpc_double_get_value(try encode(Float.signalingNaN)) | ||
XCTAssert(result.isNaN) | ||
// IDK if this is intended or acceptable, but `Double(Float.signalingNaN).isSignalingNaN` returns `false` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Huh, but it works properly when nested in an array or dictionary?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🤷
try assert(sampleUUID, encodesEqualTo: expectedXPCUUID) | ||
} | ||
|
||
func testEncodes_NilValues_as_XPCNull() throws { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, is there any value in doing this for all of these different types? It seems like what this is actually testing is Swift's implementation of Optional
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In effect; kind of. But that's not something that I knew from black box testing this (I wrote as much of this as possible without biasing myself by looking at the implementation)
Reply to first comment
Uhhhh I didn't know this. What's the difference?
Indeed, I was just scratching the surface
I didn't quite understand this, but I'll come back to it.
I wasn't aware. Could you link to something that explains the difference?
Got any ideas on how to test that? Just make a sample class hierarchy, and encode/decode it?
Will-do! |
XPC Services use "plain" XPC for lack of a better term. In the C API, this corresponds to an app using the The way this communication works means it's inherently secure because all communication is scoped by In contrast XPC for a Mach service uses the This is SecureXPC's reason for existing — to make it dead simple to secure these Mach service connections. Hence the intro part of the README calling out, "This framework is ideal for communicating with helper tools installed via SMJobBless."
No worries, happy to discuss in-depth in the Discussions section.
Apologies, what I wrote was misleading bordering on just incorrect. The sandbox is the same, but what's new since then is non-Mac App Store apps need to use the hardened runtime (in order to be notarized) and that interacts with the sandbox. By default this results in inheriting entitlements. This Apple Developer thread explains what's going on. I'm no expert in this space, but happy to discuss further in the Discussions section.
Yeah so long as the super class(es) encode (at least some of) their own properties that should do it. |
But as far as I can tell, nothing stops you from using the major parts (defining routes, serializing values) with non-Mach XPC services, right? This library just doesn't have a wrapper around My main draw to it was the Codable support, removing my need to rewrite all my structs into NSObject subclasses haha. |
c7b9a2e
to
62ee343
Compare
I've followed up on all the PR comments and made the appropriate changes.
I haven't added any tests for structs/classes. I would like to follow up on that and add it into a new PR. How do you feel about merging this work as it is, so far? |
I'm glad you've found the As far as I know there's nothing stopping the use of serialization and routes with "plain" XPC communication, although I've never tried to use it that way. Overall I imagine it's net simpler: no security considerations and the on wire protocol should be able to change without being a breaking change because an XPC Service always runs from inside the app bundle itself. There are some changes I've been considering for routes which don't make sense for XPC Services (they have to do with versioning), but in all likelihood they could be done in an optional way and just not be applicable for this type. To the extent there's some complexity here it's about how to keep the API surface simple and minimize developers using the wrong type of connection by mistake. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good, thanks for writing this and incorporating feedback!
What it says on the tin.