| // 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. | 
 |  | 
 | // KEEP THIS SYNCHRONIZED WITH ../../lib/web_ui/test/channel_buffers_test.dart | 
 |  | 
 | import 'dart:async'; | 
 | import 'dart:convert'; | 
 | import 'dart:typed_data'; | 
 | import 'dart:ui' as ui; | 
 |  | 
 | import 'package:litetest/litetest.dart'; | 
 |  | 
 | ByteData _makeByteData(String str) { | 
 |   final Uint8List list = utf8.encode(str); | 
 |   final ByteBuffer buffer = list.buffer; | 
 |   return ByteData.view(buffer); | 
 | } | 
 |  | 
 | void _resize(ui.ChannelBuffers buffers, String name, int newSize) { | 
 |   buffers.handleMessage(_makeByteData('resize\r$name\r$newSize')); | 
 | } | 
 |  | 
 | void main() { | 
 |   bool assertsEnabled = false; | 
 |   assert(() { | 
 |     assertsEnabled = true; | 
 |     return true; | 
 |   }()); | 
 |  | 
 |   test('push drain', () async { | 
 |     const String channel = 'foo'; | 
 |     final ByteData data = _makeByteData('bar'); | 
 |     final ui.ChannelBuffers buffers = ui.ChannelBuffers(); | 
 |     bool called = false; | 
 |     void callback(ByteData? responseData) { | 
 |       called = true; | 
 |     } | 
 |     buffers.push(channel, data, callback); | 
 |     // Ignoring the deprecated member use because we're specifically testing | 
 |     // deprecated API. | 
 |     // ignore: deprecated_member_use | 
 |     await buffers.drain(channel, (ByteData? drainedData, ui.PlatformMessageResponseCallback drainedCallback) async { | 
 |       expect(drainedData, equals(data)); | 
 |       expect(called, isFalse); | 
 |       drainedCallback(drainedData); | 
 |       expect(called, isTrue); | 
 |     }); | 
 |   }); | 
 |  | 
 |   test('drain is sync', () async { | 
 |     const String channel = 'foo'; | 
 |     final ByteData data = _makeByteData('message'); | 
 |     final ui.ChannelBuffers buffers = ui.ChannelBuffers(); | 
 |     void callback(ByteData? responseData) {} | 
 |     buffers.push(channel, data, callback); | 
 |     final List<String> log = <String>[]; | 
 |     final Completer<void> completer = Completer<void>(); | 
 |     scheduleMicrotask(() { log.add('before drain, microtask'); }); | 
 |     log.add('before drain'); | 
 |  | 
 |     // Ignoring the returned future because the completion of the drain is | 
 |     // communicated using the `completer`. | 
 |     // Ignoring the deprecated member use because we're specifically testing | 
 |     // deprecated API. | 
 |     // ignore: deprecated_member_use | 
 |     buffers.drain(channel, (ByteData? drainedData, ui.PlatformMessageResponseCallback drainedCallback) async { | 
 |       log.add('callback'); | 
 |       completer.complete(); | 
 |     }); | 
 |     log.add('after drain, before await'); | 
 |     await completer.future; | 
 |     log.add('after await'); | 
 |     expect(log, <String>[ | 
 |       'before drain', | 
 |       'callback', | 
 |       'after drain, before await', | 
 |       'before drain, microtask', | 
 |       'after await' | 
 |     ]); | 
 |   }); | 
 |  | 
 |   test('push drain zero', () async { | 
 |     const String channel = 'foo'; | 
 |     final ByteData data = _makeByteData('bar'); | 
 |     final | 
 |     ui.ChannelBuffers buffers = ui.ChannelBuffers(); | 
 |     void callback(ByteData? responseData) {} | 
 |     _resize(buffers, channel, 0); | 
 |     buffers.push(channel, data, callback); | 
 |     bool didCall = false; | 
 |     // Ignoring the deprecated member use because we're specifically testing | 
 |     // deprecated API. | 
 |     // ignore: deprecated_member_use | 
 |     await buffers.drain(channel, (ByteData? drainedData, ui.PlatformMessageResponseCallback drainedCallback) async { | 
 |       didCall = true; | 
 |     }); | 
 |     expect(didCall, equals(false)); | 
 |   }); | 
 |  | 
 |   test('drain when empty', () async { | 
 |     const String channel = 'foo'; | 
 |     final ui.ChannelBuffers buffers = ui.ChannelBuffers(); | 
 |     bool didCall = false; | 
 |     // Ignoring the deprecated member use because we're specifically testing | 
 |     // deprecated API. | 
 |     // ignore: deprecated_member_use | 
 |     await buffers.drain(channel, (ByteData? drainedData, ui.PlatformMessageResponseCallback drainedCallback) async { | 
 |       didCall = true; | 
 |     }); | 
 |     expect(didCall, equals(false)); | 
 |   }); | 
 |  | 
 |   test('overflow', () async { | 
 |     const String channel = 'foo'; | 
 |     final ByteData one = _makeByteData('one'); | 
 |     final ByteData two = _makeByteData('two'); | 
 |     final ByteData three = _makeByteData('three'); | 
 |     final ByteData four = _makeByteData('four'); | 
 |     final ui.ChannelBuffers buffers = ui.ChannelBuffers(); | 
 |     void callback(ByteData? responseData) {} | 
 |     _resize(buffers, channel, 3); | 
 |     buffers.push(channel, one, callback); | 
 |     buffers.push(channel, two, callback); | 
 |     buffers.push(channel, three, callback); | 
 |     buffers.push(channel, four, callback); | 
 |     int counter = 0; | 
 |     // Ignoring the deprecated member use because we're specifically testing | 
 |     // deprecated API. | 
 |     // ignore: deprecated_member_use | 
 |     await buffers.drain(channel, (ByteData? drainedData, ui.PlatformMessageResponseCallback drainedCallback) async { | 
 |       switch (counter) { | 
 |         case 0: | 
 |           expect(drainedData, equals(two)); | 
 |         case 1: | 
 |           expect(drainedData, equals(three)); | 
 |         case 2: | 
 |           expect(drainedData, equals(four)); | 
 |       } | 
 |       counter += 1; | 
 |     }); | 
 |     expect(counter, equals(3)); | 
 |   }); | 
 |  | 
 |   test('resize drop', () async { | 
 |     const String channel = 'foo'; | 
 |     final ByteData one = _makeByteData('one'); | 
 |     final ByteData two = _makeByteData('two'); | 
 |     final ui.ChannelBuffers buffers = ui.ChannelBuffers(); | 
 |     _resize(buffers, channel, 100); | 
 |     void callback(ByteData? responseData) {} | 
 |     buffers.push(channel, one, callback); | 
 |     buffers.push(channel, two, callback); | 
 |     _resize(buffers, channel, 1); | 
 |     int counter = 0; | 
 |     // Ignoring the deprecated member use because we're specifically testing | 
 |     // deprecated API. | 
 |     // ignore: deprecated_member_use | 
 |     await buffers.drain(channel, (ByteData? drainedData, ui.PlatformMessageResponseCallback drainedCallback) async { | 
 |       switch (counter) { | 
 |         case 0: | 
 |           expect(drainedData, equals(two)); | 
 |       } | 
 |       counter += 1; | 
 |     }); | 
 |     expect(counter, equals(1)); | 
 |   }); | 
 |  | 
 |   test('resize dropping calls callback', () async { | 
 |     const String channel = 'foo'; | 
 |     final ByteData one = _makeByteData('one'); | 
 |     final ByteData two = _makeByteData('two'); | 
 |     final ui.ChannelBuffers buffers = ui.ChannelBuffers(); | 
 |     bool didCallCallback = false; | 
 |     void oneCallback(ByteData? responseData) { | 
 |       expect(responseData, isNull); | 
 |       didCallCallback = true; | 
 |     } | 
 |     void twoCallback(ByteData? responseData) { | 
 |       fail('wrong callback called'); | 
 |     } | 
 |     _resize(buffers, channel, 100); | 
 |     buffers.push(channel, one, oneCallback); | 
 |     buffers.push(channel, two, twoCallback); | 
 |     expect(didCallCallback, equals(false)); | 
 |     _resize(buffers, channel, 1); | 
 |     expect(didCallCallback, equals(true)); | 
 |   }); | 
 |  | 
 |   test('overflow calls callback', () async { | 
 |     const String channel = 'foo'; | 
 |     final ByteData one = _makeByteData('one'); | 
 |     final ByteData two = _makeByteData('two'); | 
 |     final ui.ChannelBuffers buffers = ui.ChannelBuffers(); | 
 |     bool didCallCallback = false; | 
 |     void oneCallback(ByteData? responseData) { | 
 |       expect(responseData, isNull); | 
 |       didCallCallback = true; | 
 |     } | 
 |     void twoCallback(ByteData? responseData) { | 
 |       fail('wrong callback called'); | 
 |     } | 
 |     _resize(buffers, channel, 1); | 
 |     buffers.push(channel, one, oneCallback); | 
 |     buffers.push(channel, two, twoCallback); | 
 |     expect(didCallCallback, equals(true)); | 
 |   }); | 
 |  | 
 |   test('handle garbage', () async { | 
 |     final ui.ChannelBuffers buffers = ui.ChannelBuffers(); | 
 |     expect(() => buffers.handleMessage(_makeByteData('asdfasdf')), | 
 |            throwsException); | 
 |   }); | 
 |  | 
 |   test('handle resize garbage', () async { | 
 |     final ui.ChannelBuffers buffers = ui.ChannelBuffers(); | 
 |     expect(() => buffers.handleMessage(_makeByteData('resize\rfoo\rbar')), | 
 |            throwsException); | 
 |   }); | 
 |  | 
 |   test('ChannelBuffers.setListener', () async { | 
 |     final List<String> log = <String>[]; | 
 |     final ui.ChannelBuffers buffers = ui.ChannelBuffers(); | 
 |     final ByteData one = _makeByteData('one'); | 
 |     final ByteData two = _makeByteData('two'); | 
 |     final ByteData three = _makeByteData('three'); | 
 |     final ByteData four = _makeByteData('four'); | 
 |     final ByteData five = _makeByteData('five'); | 
 |     final ByteData six = _makeByteData('six'); | 
 |     final ByteData seven = _makeByteData('seven'); | 
 |     buffers.push('a', one, (ByteData? data) { }); | 
 |     buffers.push('b', two, (ByteData? data) { }); | 
 |     buffers.push('a', three, (ByteData? data) { }); | 
 |     log.add('top'); | 
 |     buffers.setListener('a', (ByteData? data, ui.PlatformMessageResponseCallback callback) { | 
 |       expect(data, isNotNull); | 
 |       log.add('a1: ${utf8.decode(data!.buffer.asUint8List())}'); | 
 |     }); | 
 |     log.add('-1'); | 
 |     await null; | 
 |     log.add('-2'); | 
 |     buffers.setListener('a', (ByteData? data, ui.PlatformMessageResponseCallback callback) { | 
 |       expect(data, isNotNull); | 
 |       log.add('a2: ${utf8.decode(data!.buffer.asUint8List())}'); | 
 |     }); | 
 |     log.add('-3'); | 
 |     await null; | 
 |     log.add('-4'); | 
 |     buffers.setListener('b', (ByteData? data, ui.PlatformMessageResponseCallback callback) { | 
 |       expect(data, isNotNull); | 
 |       log.add('b: ${utf8.decode(data!.buffer.asUint8List())}'); | 
 |     }); | 
 |     log.add('-5'); | 
 |     await null; // first microtask after setting listener drains the first message | 
 |     await null; // second microtask ends the draining. | 
 |     log.add('-6'); | 
 |     buffers.push('b', four, (ByteData? data) { }); | 
 |     buffers.push('a', five, (ByteData? data) { }); | 
 |     log.add('-7'); | 
 |     await null; | 
 |     log.add('-8'); | 
 |     buffers.clearListener('a'); | 
 |     buffers.push('a', six, (ByteData? data) { }); | 
 |     buffers.push('b', seven, (ByteData? data) { }); | 
 |     await null; | 
 |     log.add('-9'); | 
 |     expect(log, <String>[ | 
 |       'top', | 
 |       '-1', | 
 |       'a1: three', | 
 |       '-2', | 
 |       '-3', | 
 |       '-4', | 
 |       '-5', | 
 |       'b: two', | 
 |       '-6', | 
 |       'b: four', | 
 |       'a2: five', | 
 |       '-7', | 
 |       '-8', | 
 |       'b: seven', | 
 |       '-9', | 
 |     ]); | 
 |   }); | 
 |  | 
 |   test('ChannelBuffers.clearListener', () async { | 
 |     final List<String> log = <String>[]; | 
 |     final ui.ChannelBuffers buffers = ui.ChannelBuffers(); | 
 |     final ByteData one = _makeByteData('one'); | 
 |     final ByteData two = _makeByteData('two'); | 
 |     final ByteData three = _makeByteData('three'); | 
 |     final ByteData four = _makeByteData('four'); | 
 |     buffers.handleMessage(_makeByteData('resize\ra\r10')); | 
 |     buffers.push('a', one, (ByteData? data) { }); | 
 |     buffers.push('a', two, (ByteData? data) { }); | 
 |     buffers.push('a', three, (ByteData? data) { }); | 
 |     log.add('-1'); | 
 |     buffers.setListener('a', (ByteData? data, ui.PlatformMessageResponseCallback callback) { | 
 |       expect(data, isNotNull); | 
 |       log.add('a1: ${utf8.decode(data!.buffer.asUint8List())}'); | 
 |     }); | 
 |     await null; // handles one | 
 |     log.add('-2'); | 
 |     buffers.clearListener('a'); | 
 |     await null; | 
 |     log.add('-3'); | 
 |     buffers.setListener('a', (ByteData? data, ui.PlatformMessageResponseCallback callback) { | 
 |       expect(data, isNotNull); | 
 |       log.add('a2: ${utf8.decode(data!.buffer.asUint8List())}'); | 
 |     }); | 
 |     log.add('-4'); | 
 |     await null; | 
 |     buffers.push('a', four, (ByteData? data) { }); | 
 |     log.add('-5'); | 
 |     await null; | 
 |     log.add('-6'); | 
 |     await null; | 
 |     log.add('-7'); | 
 |     await null; | 
 |     expect(log, <String>[ | 
 |       '-1', | 
 |       'a1: one', | 
 |       '-2', | 
 |       '-3', | 
 |       '-4', | 
 |       'a2: two', | 
 |       '-5', | 
 |       'a2: three', | 
 |       '-6', | 
 |       'a2: four', | 
 |       '-7', | 
 |     ]); | 
 |   }); | 
 |  | 
 |   test('ChannelBuffers.handleMessage for resize', () async { | 
 |     final List<String> log = <String>[]; | 
 |     final ui.ChannelBuffers buffers = _TestChannelBuffers(log); | 
 |     // Created as follows: | 
 |     //   print(StandardMethodCodec().encodeMethodCall(MethodCall('resize', ['abcdef', 12345])).buffer.asUint8List()); | 
 |     // ...with three 0xFF bytes on either side to ensure the method works with an offset on the underlying buffer. | 
 |     buffers.handleMessage(ByteData.sublistView(Uint8List.fromList(<int>[255, 255, 255, 7, 6, 114, 101, 115, 105, 122, 101, 12, 2, 7, 6, 97, 98, 99, 100, 101, 102, 3, 57, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255]), 3, 27)); | 
 |     expect(log, const <String>['resize abcdef 12345']); | 
 |   }); | 
 |  | 
 |   test('ChannelBuffers.handleMessage for overflow', () async { | 
 |     final List<String> log = <String>[]; | 
 |     final ui.ChannelBuffers buffers = _TestChannelBuffers(log); | 
 |     // Created as follows: | 
 |     //   print(StandardMethodCodec().encodeMethodCall(MethodCall('overflow', ['abcdef', false])).buffer.asUint8List()); | 
 |     // ...with three 0xFF bytes on either side to ensure the method works with an offset on the underlying buffer. | 
 |     buffers.handleMessage(ByteData.sublistView(Uint8List.fromList(<int>[255, 255, 255, 7, 8, 111, 118, 101, 114, 102, 108, 111, 119, 12, 2, 7, 6, 97, 98, 99, 100, 101, 102, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255]), 3, 24)); | 
 |     expect(log, const <String>['allowOverflow abcdef false']); | 
 |   }); | 
 |  | 
 |   test('ChannelBuffers uses the right zones', () async { | 
 |     final List<String> log = <String>[]; | 
 |     final ui.ChannelBuffers buffers = ui.ChannelBuffers(); | 
 |     final Zone zone1 = Zone.current.fork(); | 
 |     final Zone zone2 = Zone.current.fork(); | 
 |     zone1.run(() { | 
 |       log.add('first zone run: ${Zone.current == zone1}'); | 
 |       buffers.setListener('a', (ByteData? data, ui.PlatformMessageResponseCallback callback) { | 
 |         log.add('callback1: ${Zone.current == zone1}'); | 
 |         callback(data); | 
 |       }); | 
 |     }); | 
 |     zone2.run(() { | 
 |       log.add('second zone run: ${Zone.current == zone2}'); | 
 |       buffers.push('a', ByteData.sublistView(Uint8List.fromList(<int>[]), 0, 0), (ByteData? data) { | 
 |         log.add('callback2: ${Zone.current == zone2}'); | 
 |       }); | 
 |     }); | 
 |     await null; | 
 |     expect(log, <String>[ | 
 |       'first zone run: true', | 
 |       'second zone run: true', | 
 |       'callback1: true', | 
 |       'callback2: true', | 
 |     ]); | 
 |   }); | 
 |  | 
 |   test('ChannelBufferspush rejects names with nulls', () async { | 
 |     const String channel = 'foo\u0000bar'; | 
 |     final ByteData blabla = _makeByteData('blabla'); | 
 |     final ui.ChannelBuffers buffers = ui.ChannelBuffers(); | 
 |     try { | 
 |       buffers.push(channel, blabla, (ByteData? data) { }); | 
 |       fail('did not throw as expected'); | 
 |     } on AssertionError catch (e) { | 
 |       expect(e.toString(), contains('U+0000 NULL')); | 
 |     } | 
 |     try { | 
 |       buffers.setListener(channel, (ByteData? data, ui.PlatformMessageResponseCallback callback) { }); | 
 |       fail('did not throw as expected'); | 
 |     } on AssertionError catch (e) { | 
 |       expect(e.toString(), contains('U+0000 NULL')); | 
 |     } | 
 |   }, skip: !assertsEnabled); | 
 | } | 
 |  | 
 | class _TestChannelBuffers extends ui.ChannelBuffers { | 
 |   _TestChannelBuffers(this.log); | 
 |  | 
 |   final List<String> log; | 
 |  | 
 |   @override | 
 |   void resize(String name, int newSize) { | 
 |     log.add('resize $name $newSize'); | 
 |   } | 
 |  | 
 |   @override | 
 |   void allowOverflow(String name, bool allowed) { | 
 |     log.add('allowOverflow $name $allowed'); | 
 |   } | 
 | } |