-
Notifications
You must be signed in to change notification settings - Fork 6k
Channel buffers #12167
Channel buffers #12167
Changes from 34 commits
12c4f89
8a8343d
cff180e
31a845b
c28999d
4253c1c
e19f59c
63f7b8a
4e87cb0
24607ab
c4bec51
ea3823f
afc09b6
0a3c609
9df8b23
f32af48
97be8ca
d041d62
1533428
42c742d
652a822
4d84d1b
33c21e9
afa7bb0
6b6dbb9
bcc5ea7
3ff518c
8d4a1e4
8e6ee3b
3e3c38d
a4204c9
1527c3b
837ed7e
23a9ce9
007edb1
741981b
571594e
478520f
8569f21
96761f8
adcd641
24cf927
67aecdb
b13a3dc
282bff0
a0a4c0e
26d9deb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,173 @@ | ||
| // Copyright 2013 The Flutter Authors. All rights reserved. | ||
| // Use of this source code is governed by a BSD-style license that can be | ||
| // found in the LICENSE file. | ||
|
|
||
| part of dart.ui; | ||
|
|
||
| /// A saved platform message for a channel with its callback. | ||
| class _StoredMessage { | ||
| /// Default constructor, takes in a [ByteData] that represents the | ||
| /// payload of the message and a [PlatformMessageResponseCallback] | ||
| /// that represents the callback that will be called when the message | ||
| /// is handled. | ||
| _StoredMessage(this._data, this._callback); | ||
|
|
||
| final ByteData _data; | ||
| final PlatformMessageResponseCallback _callback; | ||
|
|
||
| /// Getter for _data field, represents the message's payload. | ||
| ByteData get data => _data; | ||
|
gaaclarke marked this conversation as resolved.
|
||
| /// Getter for the _callback field, to be called when the message is received. | ||
| PlatformMessageResponseCallback get callback => _callback; | ||
|
gaaclarke marked this conversation as resolved.
|
||
| } | ||
|
|
||
| /// A fixed-size circular queue. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A per channel, fixed-size circular queue.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There isn't anything about the implementation that requires this to be used for channels. It's a generic RingBuffer that could be used anywhere. |
||
| class _RingBuffer<T> { | ||
| final collection.ListQueue<T> _queue; | ||
| int _capacity; | ||
| Function(T) _dropItemCallback; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not clear what this should be. Add doc.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done, missed responding to this last night. |
||
|
|
||
| _RingBuffer(this._capacity) | ||
| : _queue = collection.ListQueue<T>(_capacity); | ||
|
|
||
| int get length => _queue.length; | ||
|
|
||
| int get capacity => _capacity; | ||
|
|
||
| bool get isEmpty => _queue.isEmpty; | ||
|
|
||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. doc
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This doesn't say anything :) https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo#avoid-useless-documentation Something like
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That seemed redundant since the docstring for _dropItemCallback says all of that. The intent was to point them to that documentation and not repeat it. I found examples elsewhere and they put the ivar next to the setter and getter and it gets collapsed in the generated documentation so I don't have to repeat it, so done
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yup, this works. |
||
| set dropItemCallback(Function(T) callback) { | ||
| _dropItemCallback = callback; | ||
| } | ||
|
|
||
| /// Returns true on overflow. | ||
| bool push(T val) { | ||
| bool overflow = false; | ||
| while (_queue.length >= _capacity) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. wouldn't you want to reuse the resize logic here or extract a part of it? There's some repetition here.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done, it wasn't trivial since the logic was a bit different and didn't buy many lines, but since you asked there you go. |
||
| final T item = _queue.removeFirst(); | ||
| if (_dropItemCallback != null) { | ||
| _dropItemCallback(item); | ||
| } | ||
| overflow = true; | ||
| } | ||
| _queue.addLast(val); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does this work if the capacity is zero? Add test.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done |
||
| return overflow; | ||
| } | ||
|
|
||
| /// Returns null when empty. | ||
| T pop() { | ||
| return _queue.isEmpty ? null : _queue.removeFirst(); | ||
| } | ||
|
|
||
| /// Returns the number of discarded items resulting from resize. | ||
| int resize(int newSize) { | ||
| int result = 0; | ||
|
|
||
| while (length > newSize) { | ||
| result += 1; | ||
| final T item = _queue.removeFirst(); | ||
| if (_dropItemCallback != null) { | ||
| _dropItemCallback(item); | ||
| } | ||
| } | ||
|
|
||
| _capacity = newSize; | ||
|
|
||
| return result; | ||
| } | ||
| } | ||
|
|
||
| typedef DrainChannelCallback = Future<void> Function(ByteData, PlatformMessageResponseCallback); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Doc. Who is supposed to use it. Use See also: for references.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||
|
|
||
| /// Storage of channel messages until the channels are completely routed, | ||
| /// i.e. when a message handler is attached to the channel on the framework side. | ||
| /// | ||
| /// Each channel has a finite buffer capacity and in a FIFO manner messages will | ||
| /// be deleted if the capacity is exceeded. The intention is that these buffers | ||
| /// will be drained once a callback is setup on the BinaryMessenger in the | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should be once a callback is setup per channel on the BinaryMessenger now right?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yep, the companion PR needs to land as well for that to be true. |
||
| /// Flutter framework. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also document how this should be used. Should users instantiate these things themselves? Who needs to hook these up? Or is everything already done implicitly. If so, reference those classes.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||
| class ChannelBuffers { | ||
|
gaaclarke marked this conversation as resolved.
|
||
| /// By default we store one message per channel. There are tradeoffs associated | ||
| /// with any size. The correct size should be chosen for the semantics of your | ||
| /// channel. | ||
| /// | ||
| /// Size 0 implies you want to ignore any message that gets sent before the engine | ||
| /// is ready (keeping in mind there is no way to know when the engine is ready). | ||
| /// | ||
| /// Size 1 implies that you only care about the most recent value. | ||
| /// | ||
| /// Size >1 means you want to process every single message and want to chose a | ||
| /// buffer size that will avoid any overflows. | ||
| static const int kDefaultBufferSize = 1; | ||
|
|
||
| final Map<String, _RingBuffer<_StoredMessage>> _messages = | ||
| <String, _RingBuffer<_StoredMessage>>{}; | ||
|
|
||
| _RingBuffer<_StoredMessage> _makeRingBuffer(int size) { | ||
| final _RingBuffer<_StoredMessage> result = _RingBuffer<_StoredMessage>(size); | ||
| result.dropItemCallback = _onDropItem; | ||
| return result; | ||
| } | ||
|
|
||
| void _onDropItem(_StoredMessage message) { | ||
| message.callback(null); | ||
| } | ||
|
|
||
| /// Returns true on overflow. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Each field of a dart class has its own dartdoc page. For example https://api.flutter.dev/flutter/dart-ui/Offset/infinite-constant.html It needs to be sufficiently complete to stand on its own.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I can't see what you are referring to in this comment.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I meant each field's doc needs to fully describe what they do, when they're used etc without the context of any surrounding fields' docs or the class doc since they appear independently when we generate dartdoc pages
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok, I added documentation even on private fields.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I didn't mean adding documentations on private fields. I just meant each public field of public classes (appearing in their own URL with their own page in the API doc website) should fully describe themselves. Consider, as example, seeing https://screenshot.googleplex.com/K4iw8SwzUuE vs https://screenshot.googleplex.com/QxCLRcoZFqY |
||
| bool push(String channel, ByteData data, PlatformMessageResponseCallback callback) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this API futureproof enough? When each channel can specify its own cache size, how would it express it? I assume via the channel constructor. When it does so, how does it pass that size int into this signature? Would we need to leak the implementation detail of this class's implementation into the channel class implementation (by forcing it to come call ChannelBuffers.resize)?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe so. For basic channels the constructor will call This comment is on the "push" method, I'm guessing it was suppose to be somewhere else.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah ok, SG |
||
| _RingBuffer<_StoredMessage> queue = _messages[channel]; | ||
| if (queue == null) { | ||
| queue = _makeRingBuffer(kDefaultBufferSize); | ||
| _messages[channel] = queue; | ||
| } | ||
| final bool result = queue.push(_StoredMessage(data, callback)); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. just call this overflowed locally if that's what it's supposed to mean
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||
| if (result) { | ||
| // TODO(aaclarke): Update this message to include instructions on how to resize | ||
| // the buffer once that is available to users. | ||
| _Logger._printString('Overflow on channel: $channel. ' | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We probably want a different message depending on whether the channel's buffer size is 0, 1 or 1+. Since I wouldn't really call it an 'overflow' if the size is 0 and it's more or less WAI if the size is 1.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You may be right. I reworded it a bit and I think the new message is better in this respect. |
||
| 'Messages on this channel are being sent faster ' | ||
| 'than they are being processed which is resulting ' | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This wording provides a bit of a red herring. It'll make the user think that somehow the Dart code isn't keeping up whereas in reality, the Flutter code isn't running (either because their main entrypoint didn't call runApp or the main entrypoint never ran).
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Reworded. |
||
| 'in the dropping of messages. The engine may not be ' | ||
| 'running or you need to adjust the buffer size.'); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. please only print to the console if it's actionable. Every message to the console should be an error that can be fixed in every circumstance.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is actionable and addressable as noted in the error message. It means they are talking profusely to a dead channel. They need to adjust the buffer size or run engine.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we could make this actionable by exposing some way for a user to set the size of the queue, and then making this print out instructions or a link to instructions on how to do that.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @dnfield That sounds good to me, I've added a todo once resizing has percolated to the user level.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should either resolve this in this PR or remove the log IMO - I think this is probably really useful right now for someone doing local engine development, but it will likely cause confusion until it's actionable (imagine, a framework developer reading a bug report about this and not being familiar with this change).
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I prefer being more transparent in the interim, @xster what's your opinion?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would gate adjusting the buffer size with a lot of care. It's rarely what the user really wants to do. Make sure we thoroughly explain the cause before recommending this. |
||
| } | ||
| return result; | ||
| } | ||
|
|
||
| /// Returns null on underflow. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When would this happen? Wouldn't consumers check isEmpty first?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. They should be calling isEmpty. You know how it is, troublemakers don't follow the protocol.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. dem delinquent kids these days |
||
| _StoredMessage _pop(String channel) { | ||
| final _RingBuffer<_StoredMessage> queue = _messages[channel]; | ||
| final _StoredMessage result = queue?.pop(); | ||
| return result; | ||
| } | ||
|
|
||
| bool _isEmpty(String channel) { | ||
| final _RingBuffer<_StoredMessage> queue = _messages[channel]; | ||
| return (queue == null) ? true : queue.isEmpty; | ||
| } | ||
|
|
||
| void resize(String channel, int newSize) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. All public Dart classes and fields need dartdocs.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done. |
||
| _RingBuffer<_StoredMessage> queue = _messages[channel]; | ||
| if (queue == null) { | ||
| queue = _makeRingBuffer(newSize); | ||
| _messages[channel] = queue; | ||
| } else { | ||
| final int numberOfDroppedMessages = queue.resize(newSize); | ||
| if (numberOfDroppedMessages > 0) { | ||
| _Logger._printString('Dropping messages on channel "$channel" as a result of shrinking the buffer size.'); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /// Remove and process all stored messages for a given channel. | ||
| /// | ||
| /// This should be called once a channel is prepared to handle messages | ||
| /// (ie when a message handler is setup in the framework). | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: i.e.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done |
||
| Future<void> drain(String channel, DrainChannelCallback callback) async { | ||
| while (!_isEmpty(channel)) { | ||
| final _StoredMessage message = _pop(channel); | ||
| await callback(message.data, message.callback); | ||
| } | ||
| } | ||
|
gaaclarke marked this conversation as resolved.
|
||
| } | ||
|
|
||
| final ChannelBuffers channelBuffers = ChannelBuffers(); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should probably not be a static but owned by someone. When there are multiple engines, each should (via some chain of ownership) end up with a different instance of buffers.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Won't each engine have its own isolate and therefore its own channelBuffers? If this is true this will also be a problem for
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yup you're right, that was a nonsensical comment :D
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Still needs a doc (for public fields)
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -161,7 +161,9 @@ void _dispatchPlatformMessage(String name, ByteData data, int responseId) { | |
| }, | ||
| ); | ||
| } else { | ||
| window._respondToPlatformMessage(responseId, null); | ||
| channelBuffers.push(name, data, (ByteData responseData) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Related to the comment on the other PR. I think there are 2 lifecycles and we can't really conflate them. There's setting the onPlatformMessage which happens once per binding, and there's adding per channel handlers which happens at arbitrary time. Once the onPlatformMessage is attached, all buffers will stop queueing. But there are no guarantee that the following sequence of actions won't happen. 1- run entrypoint, entrypoint runApps, which attaches window.onPlatformMessage. It probably works now since the synchronous execution of runApp+binding constructor+our system channels setting handler all happens at the same time. But we can't guarantee that for users' own channels.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've addressed this in the other PR. Both cases get queued up now. |
||
| window._respondToPlatformMessage(responseId, responseData); | ||
| }); | ||
| } | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| // Copyright 2013 The Flutter Authors. All rights reserved. | ||
| // Use of this source code is governed by a BSD-style license that can be | ||
| // found in the LICENSE file. | ||
|
|
||
| part of ui; | ||
|
|
||
| typedef DrainChannelCallback = Future<void> Function(ByteData, PlatformMessageResponseCallback); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. dartdoc
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure what you are referring to. I updated the docstring to match the ones found in |
||
|
|
||
| /// Storage of channel messages until the channels are completely routed, | ||
| /// i.e. when a message handler is attached to the channel on the framework side. | ||
| /// | ||
| /// Each channel has a finite buffer capacity and in a FIFO manner messages will | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Make this doc correct (i.e. this class doesn't do anything on web)
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done |
||
| /// be deleted if the capacity is exceeded. The intention is that these buffers | ||
| /// will be drained once a callback is setup on the BinaryMessenger in the | ||
| /// Flutter framework. | ||
| class ChannelBuffers { | ||
| /// Returns true on overflow. | ||
| bool push(String channel, ByteData data, PlatformMessageResponseCallback callback) { | ||
| callback(null); | ||
| return true; | ||
| } | ||
|
|
||
| void resize(String channel, int newSize) {} | ||
|
|
||
| /// Remove and process all stored messages for a given channel. | ||
| /// | ||
| /// This should be called once a channel is prepared to handle messages | ||
| /// (ie when a message handler is setup in the framework). | ||
| Future<void> drain(String channel, DrainChannelCallback callback) async { | ||
| } | ||
| } | ||
|
|
||
| final ChannelBuffers channelBuffers = ChannelBuffers(); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,120 @@ | ||
| import 'dart:ui' as ui; | ||
| import 'dart:typed_data'; | ||
| import 'dart:convert'; | ||
|
|
||
| import 'package:test/test.dart'; | ||
|
|
||
| void main() { | ||
|
gaaclarke marked this conversation as resolved.
|
||
|
|
||
| ByteData _makeByteData(String str) { | ||
| var list = utf8.encode(str); | ||
| var buffer = list is Uint8List ? list.buffer : new Uint8List.fromList(list).buffer; | ||
| return ByteData.view(buffer); | ||
| } | ||
|
|
||
| String _getString(ByteData data) { | ||
| final buffer = data.buffer; | ||
| var list = buffer.asUint8List(data.offsetInBytes, data.lengthInBytes); | ||
| return utf8.decode(list); | ||
| } | ||
|
|
||
| test('push drain', () async { | ||
| String channel = "foo"; | ||
| ByteData data = _makeByteData('bar'); | ||
| ui.ChannelBuffers buffers = ui.ChannelBuffers(); | ||
| ui.PlatformMessageResponseCallback callback = (ByteData responseData) {}; | ||
| buffers.push(channel, data, callback); | ||
| await buffers.drain(channel, (ByteData drainedData, ui.PlatformMessageResponseCallback drainedCallback) { | ||
| expect(drainedData, equals(data)); | ||
| expect(drainedCallback, equals(callback)); | ||
| }); | ||
| }); | ||
|
|
||
| test('empty', () async { | ||
| String channel = "foo"; | ||
| ByteData data = _makeByteData('bar'); | ||
| ui.ChannelBuffers buffers = ui.ChannelBuffers(); | ||
| ui.PlatformMessageResponseCallback callback = (ByteData responseData) {}; | ||
| bool didCall = false; | ||
| await buffers.drain(channel, (ByteData drainedData, ui.PlatformMessageResponseCallback drainedCallback) { | ||
| didCall = true; | ||
| }); | ||
| expect(didCall, equals(false)); | ||
| }); | ||
|
|
||
| test('overflow', () async { | ||
| String channel = "foo"; | ||
| ByteData one = _makeByteData('one'); | ||
| ByteData two = _makeByteData('two'); | ||
| ByteData three = _makeByteData('three'); | ||
| ByteData four = _makeByteData('four'); | ||
| ui.ChannelBuffers buffers = ui.ChannelBuffers(); | ||
| ui.PlatformMessageResponseCallback callback = (ByteData responseData) {}; | ||
| buffers.resize(channel, 3); | ||
| expect(buffers.push(channel, one, callback), equals(false)); | ||
| expect(buffers.push(channel, two, callback), equals(false)); | ||
| expect(buffers.push(channel, three, callback), equals(false)); | ||
| expect(buffers.push(channel, four, callback), equals(true)); | ||
| int counter = 0; | ||
| await buffers.drain(channel, (ByteData drainedData, ui.PlatformMessageResponseCallback drainedCallback) { | ||
| if (counter++ == 0) { | ||
| expect(drainedData, equals(two)); | ||
| expect(drainedCallback, equals(callback)); | ||
| } | ||
| }); | ||
| expect(counter, equals(3)); | ||
| }); | ||
|
|
||
| test('resize drop', () async { | ||
| String channel = "foo"; | ||
| ByteData one = _makeByteData('one'); | ||
| ByteData two = _makeByteData('two'); | ||
| ui.ChannelBuffers buffers = ui.ChannelBuffers(); | ||
| buffers.resize(channel, 100); | ||
| ui.PlatformMessageResponseCallback callback = (ByteData responseData) {}; | ||
| expect(buffers.push(channel, one, callback), equals(false)); | ||
| expect(buffers.push(channel, two, callback), equals(false)); | ||
| buffers.resize(channel, 1); | ||
| int counter = 0; | ||
| await buffers.drain(channel, (ByteData drainedData, ui.PlatformMessageResponseCallback drainedCallback) { | ||
| if (counter++ == 0) { | ||
| expect(drainedData, equals(two)); | ||
| expect(drainedCallback, equals(callback)); | ||
| } | ||
| }); | ||
| expect(counter, equals(1)); | ||
| }); | ||
|
|
||
| test('resize dropping calls callback', () async { | ||
| String channel = "foo"; | ||
| ByteData one = _makeByteData('one'); | ||
| ByteData two = _makeByteData('two'); | ||
| ui.ChannelBuffers buffers = ui.ChannelBuffers(); | ||
| bool didCallCallback = false; | ||
| ui.PlatformMessageResponseCallback oneCallback = (ByteData responseData) { | ||
| didCallCallback = true; | ||
| }; | ||
| ui.PlatformMessageResponseCallback twoCallback = (ByteData responseData) {}; | ||
| buffers.resize(channel, 100); | ||
| expect(buffers.push(channel, one, oneCallback), equals(false)); | ||
| expect(buffers.push(channel, two, twoCallback), equals(false)); | ||
| buffers.resize(channel, 1); | ||
| expect(didCallCallback, equals(true)); | ||
| }); | ||
|
|
||
| test('overflow calls callback', () async { | ||
| String channel = "foo"; | ||
| ByteData one = _makeByteData('one'); | ||
| ByteData two = _makeByteData('two'); | ||
| ui.ChannelBuffers buffers = ui.ChannelBuffers(); | ||
| bool didCallCallback = false; | ||
| ui.PlatformMessageResponseCallback oneCallback = (ByteData responseData) { | ||
| didCallCallback = true; | ||
| }; | ||
| ui.PlatformMessageResponseCallback twoCallback = (ByteData responseData) {}; | ||
| buffers.resize(channel, 1); | ||
| expect(buffers.push(channel, one, oneCallback), equals(false)); | ||
| expect(buffers.push(channel, two, twoCallback), equals(true)); | ||
| expect(didCallCallback, equals(true)); | ||
| }); | ||
| } | ||
|
gaaclarke marked this conversation as resolved.
|
||
Uh oh!
There was an error while loading. Please reload this page.