// Copyright 2014 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. import 'dart:async'; import 'dart:typed_data'; import 'package:fake_async/fake_async.dart'; import 'package:flutter_tools/src/proxied_devices/debounce_data_stream.dart'; import '../../src/common.dart'; void main() { group('debounceDataStreams', () { late FakeAsync fakeAsync; late StreamController source; late Stream output; const debounceDuration = Duration(seconds: 10); const smallDuration = Duration(milliseconds: 10); void addToSource(int value) { source.add(Uint8List.fromList([value])); } setUp(() { fakeAsync = FakeAsync(); fakeAsync.run((FakeAsync time) { source = StreamController(); output = debounceDataStream(source.stream, debounceDuration); }); }); testWithoutContext('does not listen if returned stream is not listened to', () { expect(source.hasListener, false); output.listen(dummy); expect(source.hasListener, true); }); testWithoutContext('forwards data normally is all data if longer than duration apart', () { fakeAsync.run((FakeAsync time) { final outputItems = []; output.listen(outputItems.add); addToSource(1); time.elapse(debounceDuration + smallDuration); addToSource(2); time.elapse(debounceDuration + smallDuration); addToSource(3); time.elapse(debounceDuration + smallDuration); expect(outputItems, >[ [1], [2], [3], ]); }); }); testWithoutContext('merge data after the first if sent within duration', () { fakeAsync.run((FakeAsync time) { final outputItems = []; output.listen(outputItems.add); addToSource(1); time.elapse(smallDuration); addToSource(2); time.elapse(smallDuration); addToSource(3); time.elapse(debounceDuration + smallDuration); expect(outputItems, >[ [1], [2, 3], ]); }); }); testWithoutContext( 'output data in separate chunks if time between them is longer than duration', () { fakeAsync.run((FakeAsync time) { final outputItems = []; output.listen(outputItems.add); addToSource(1); time.elapse(smallDuration); addToSource(2); time.elapse(smallDuration); addToSource(3); time.elapse(debounceDuration + smallDuration); addToSource(4); time.elapse(smallDuration); addToSource(5); time.elapse(debounceDuration + smallDuration); expect(outputItems, >[ [1], [2, 3], [4, 5], ]); }); }, ); testWithoutContext('sends the last chunk after debounce duration', () { fakeAsync.run((FakeAsync time) { final outputItems = []; output.listen(outputItems.add); addToSource(1); time.flushMicrotasks(); expect(outputItems, >[ [1], ]); time.elapse(smallDuration); addToSource(2); time.elapse(smallDuration); addToSource(3); expect(outputItems, >[ [1], ]); time.elapse(debounceDuration + smallDuration); expect(outputItems, >[ [1], [2, 3], ]); }); }); testWithoutContext('close if source stream is closed', () { fakeAsync.run((FakeAsync time) { var isDone = false; output.listen(dummy, onDone: () => isDone = true); expect(isDone, false); source.close(); time.flushMicrotasks(); expect(isDone, true); }); }); testWithoutContext('delay close until after last chunk is sent', () { fakeAsync.run((FakeAsync time) { final outputItems = []; var isDone = false; output.listen(outputItems.add, onDone: () => isDone = true); addToSource(1); time.flushMicrotasks(); expect(outputItems, >[ [1], ]); addToSource(2); source.close(); time.elapse(smallDuration); expect(isDone, false); expect(outputItems, >[ [1], ]); time.elapse(debounceDuration + smallDuration); expect(outputItems, >[ [1], [2], ]); expect(isDone, true); }); }); testWithoutContext('close if returned stream is closed', () { fakeAsync.run((FakeAsync time) { var isCancelled = false; source.onCancel = () => isCancelled = true; final StreamSubscription subscription = output.listen(dummy); expect(isCancelled, false); subscription.cancel(); expect(isCancelled, true); }); }); }); } Uint8List dummy(Uint8List data) => data;