-
-
Notifications
You must be signed in to change notification settings - Fork 487
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
Ended isolate + unit tests for it #735
Conversation
I'll update that PR a bit later with the latest updates from the one-isolate |
@ryanheise WDYT about moving onNotificationClicked into the handler? That way we could broadcast it to isolates, and honestly, I don't really see why it shouldn't be there, anything that is related to the interaction with the platform events should be abstracted into handler If yes, I can do that in this PR |
The right thing to do here is not completely obvious.Considering the Android model, the notification click launch is something different. The Handler maps to features of the service, while the notification click launch maps to features of the activity, but you could shoehorn it into the handler and it would work. I don't have a crystal clear view on what is the right approach here, but sticking it in the handler I guess is good enough if for no other reason than that it's where we're trying to stick everything else (although there are still some exceptions. |
Got it. Actually, I think it's better to move it separetly for easier review |
Currently if the handler was hosted with |
Is there a reason for making some private APIs public? E.g. |
That allows the extension of the I actually have no objection on that and I will be fine to make it private |
In that case I'd prefer to leave them as private and then wait and see what (if any) limitations people run into with the public API. |
Any thoughts on that? #735 (comment) |
I made the handler private in the recent commit |
argumentsLog.clear(); | ||
} | ||
|
||
void _log(String method, [List<Object?> arguments = const [null]]) { |
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 actually should be an empty array instead. I have already changed this on another branch that adds platform connection tests over this PR, so I keep it as is and will change it in my next PR
There were a few places where you didn't change the nullability of the queue, I fixed them while merging |
Is there a reason why the isolate unit tests couldn't use |
There are a lot of unrelated things in there, how do you see that? |
I'm just looking at whether any of the Perhaps there is an argument to break down the design of audio_service into genuine "units" so that they can be better tested in units, and maybe that is what inspired your idea to make the isolate hosting API public? If so, then I understand the benefit of that. I don't necessarily see any use case for a general purpose hosting API, but there is a benefit to splitting |
I have worked out an API I'm happy with. I want to improve the stream sync logic and improve the encapsulation before committing, but the unit tests will have to be updated afterwards. |
What's this improvement to the sync logic I wonder? |
Several things. Future<void> init() async {
if (testSyncIsolate ||
!kDebugMode ||
!Platform.environment.containsKey('FLUTTER_TEST')) {
await Future.wait([
syncSubject(playbackState, 'playbackState'),
syncSubject(queue, 'queue'),
syncSubject(queueTitle, 'queueTitle'),
syncSubject(mediaItem, 'mediaItem'),
syncSubject(androidPlaybackInfo, 'androidPlaybackInfo'),
syncSubject(ratingStyle, 'ratingStyle'),
syncSubject(customEvent, 'customEvent'),
syncSubject(customState, 'customState'),
]);
}
} That condition is overly complex, and I think any version of what it's trying to do is undesirable. Instead, Aside from that, there is something wrong with the unit tests trying to add events to subjects on the client side - they should only be added on the supplier side. This should be unidirectional communication where events are only added by the supplier and consumed by the client. The |
That sounds fine, but if it breaks tests it might be not fine
I'm not sure I understand what you mean. Subjects are both directional i.e. both host and connector isolate can be a modifier of them. If you make them only one directional, you won't be able to modfiy the subject, e.g. |
Can you get a little more specific what is complicated there? |
Why would a FIFO need to skip anything? |
I just noticed your other message in my email inbox which helps provide me with further context behind your question. Yes, this is genuinely intended to work in one direction. The Edit: Oh, I see that message is above, not just in email. |
I'd argue that's a FIFO, since we are only really interested in notifying about the most recent states, that's a tradeoff because isolate messages are not instant EDIT: I mean, technically inside one isolate, it is a FIFO, but we need to consider these things below, so some updates should be omitted Two main reasons the
|
I would consider this a serious limitation of the API |
That would break encapsulation. When the question arises as to who should be responsible for updating, say, the Let's say in this client/server model the server side is in another process, then for other obvious reasons, the client cannot be allowed to update It has always been this way, and I do not consider it to be a serious limitation of the API but rather safe design. Whatever your use case, I'm sure it can fit into this model. |
It's exactly what happens right now, the only difference is that we notify the subject about the updates that are made in them in their isolates, since there's no need to send this to the host isolate and wait before it responds. It is a server and coordinates all the updates though
I'm not sure what is "another process", but if you can't reach out to the main isolate, the
So, it is by design that you can't call for example |
I don't see any problem with that, is there? |
Let's step back a bit, I will just give my perspective on what I meant
Eventually, I would like this client/server model to allow connecting to the media session of another Android process. The client/server model is a general concept that applies in a number of different scenarios: 1) within the same isolate, 2) between isolates, 3) between processes, 4) between devices:
Oh dear. "Encapsulation"? I thought we were on the same page. https://www.topperskills.com/tutorials/oop/object-oriented-programming-encapsulation-concepts.html
I have no objection to implementing this same concept slightly differently by replacing the methods by an "input" stream (sort of like the BLoC architecture works), but still that is logically equivalent to the same thing, and I would never want to break encapsulation of the state by allowing anyone outside the capsule to directly write to the state. I don't think we should continue this discussion though. If we're going to debate the value of encapsulation, I'll just have to make a final decision on the matter and say I prefer to encapsulate the state rather than make it globally writable. |
Can you explain what you mean by encapsulation here? I don't understand, since if I extend from the BaseAudioHandler, everything is encapsulated. I just don't get why you want it to be AudioHandler necessarily. If you reject this design though I don't feel I want to work on this PR, sorry. You didn't have any real argument against it, you started talking about connection from other processes, which is totally unrelated to this PR. The only argument I heard is that you just don't prefer this style, I totally agree I don't want to discuss stylistic preferences here. |
I read it again and now I understand a bit what you mean by encapsulation, but that's the way you designed BaseAudioHandler, so probably you want to redesign it to avoid any "direct" writes to the subjects (I personally would consider that as pain in the back) |
Encapsulation is part of the standard vocabulary of a software engineer, and that enables productive and compact design discussions. The word itself shouldn't need an explanation among qualified engineers, rather, the word is itself the compact explanation used to justify other higher level design decisions. So all we need for the scope of this discussion is that, due to encapsulation, this is undesirable: handler.queue.add(newQueue); while this is good: handler.updateQueue(newQueue); with Future<void> updateQueue(List<MediaItem> queue) async {
this.queue.add(queue);
} You may not see the benefit, but, as a shorthand, I'll say the benefit of the above is "all of the benefits of encapsulation", which you can look up elsewhere to save time here.
Everyone goes through that phase, I suppose, until they have one or two experiences where rejecting encapsulation caused an even bigger pain in the back. Anyway, I want to say that regarding this pull request, I am happy with it and I have already merged it into my local branch and started making the improvements mentioned above to encapsulation and stream syncing. Although the encapsulation issue needs to be fixed, that does not negate the value of this contribution - but if you would prefer me not to merge it, let me know and I'll respect that. |
I've made my API changes to the |
I saw you decided to write unit tests from scratch. FYI I wouldn't recommend not testing with which parameters methods were called since while I was making another PR I discovered that a few methods were missing extras in platform interop. |
Finishes the isolate API
Also adds MockBaseAudioHandler
Fixes #733edit: this issue was closed and PR was changed, see #733 (comment)Related #716