// 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:io' as io; import 'package:flutter_tools/src/base/io.dart'; import 'package:flutter_tools/src/base/signals.dart'; import 'package:mockito/mockito.dart'; import '../../src/common.dart'; void main() { group('Signals', () { Signals signals; MockIoProcessSignal mockSignal; ProcessSignal signalUnderTest; StreamController controller; setUp(() { signals = Signals.test(); mockSignal = MockIoProcessSignal(); signalUnderTest = ProcessSignal(mockSignal); controller = StreamController(); when(mockSignal.watch()).thenAnswer((Invocation invocation) => controller.stream); }); testWithoutContext('signal handler runs', () async { final Completer completer = Completer(); signals.addHandler(signalUnderTest, (ProcessSignal s) { expect(s, signalUnderTest); completer.complete(); }); controller.add(mockSignal); await completer.future; }); testWithoutContext('signal handlers run in order', () async { final Completer completer = Completer(); bool first = false; signals.addHandler(signalUnderTest, (ProcessSignal s) { expect(s, signalUnderTest); first = true; }); signals.addHandler(signalUnderTest, (ProcessSignal s) { expect(s, signalUnderTest); expect(first, isTrue); completer.complete(); }); controller.add(mockSignal); await completer.future; }); testWithoutContext('signal handler error goes on error stream', () async { final Exception exn = Exception('Error'); signals.addHandler(signalUnderTest, (ProcessSignal s) { throw exn; }); final Completer completer = Completer(); final List errList = []; final StreamSubscription errSub = signals.errors.listen( (Object err) { errList.add(err); completer.complete(); }, ); controller.add(mockSignal); await completer.future; await errSub.cancel(); expect(errList, contains(exn)); }); testWithoutContext('removed signal handler does not run', () async { final Object token = signals.addHandler( signalUnderTest, (ProcessSignal s) { fail('Signal handler should have been removed.'); }, ); await signals.removeHandler(signalUnderTest, token); final List errList = []; final StreamSubscription errSub = signals.errors.listen( (Object err) { errList.add(err); }, ); controller.add(mockSignal); await errSub.cancel(); expect(errList, isEmpty); }); testWithoutContext('non-removed signal handler still runs', () async { final Completer completer = Completer(); signals.addHandler(signalUnderTest, (ProcessSignal s) { expect(s, signalUnderTest); completer.complete(); }); final Object token = signals.addHandler( signalUnderTest, (ProcessSignal s) { fail('Signal handler should have been removed.'); }, ); await signals.removeHandler(signalUnderTest, token); final List errList = []; final StreamSubscription errSub = signals.errors.listen( (Object err) { errList.add(err); }, ); controller.add(mockSignal); await completer.future; await errSub.cancel(); expect(errList, isEmpty); }); testWithoutContext('only handlers for the correct signal run', () async { final MockIoProcessSignal mockSignal2 = MockIoProcessSignal(); final StreamController controller2 = StreamController(); final ProcessSignal otherSignal = ProcessSignal(mockSignal2); when(mockSignal2.watch()).thenAnswer((Invocation invocation) => controller2.stream); final Completer completer = Completer(); signals.addHandler(signalUnderTest, (ProcessSignal s) { expect(s, signalUnderTest); completer.complete(); }); signals.addHandler(otherSignal, (ProcessSignal s) { fail('Wrong signal!.'); }); final List errList = []; final StreamSubscription errSub = signals.errors.listen( (Object err) { errList.add(err); }, ); controller.add(mockSignal); await completer.future; await errSub.cancel(); expect(errList, isEmpty); }); testWithoutContext('all handlers for exiting signals are run before exit', () async { final Signals signals = Signals.test( exitSignals: [signalUnderTest], ); final Completer completer = Completer(); bool first = false; bool second = false; setExitFunctionForTests((int exitCode) { // Both handlers have run before exit is called. expect(first, isTrue); expect(second, isTrue); expect(exitCode, 0); restoreExitFunction(); completer.complete(); }); signals.addHandler(signalUnderTest, (ProcessSignal s) { expect(s, signalUnderTest); expect(first, isFalse); expect(second, isFalse); first = true; }); signals.addHandler(signalUnderTest, (ProcessSignal s) { expect(s, signalUnderTest); expect(first, isTrue); expect(second, isFalse); second = true; }); controller.add(mockSignal); await completer.future; }); }); } class MockIoProcessSignal extends Mock implements io.ProcessSignal {}