signals_test.dart 6.25 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1
// Copyright 2014 The Flutter Authors. All rights reserved.
2 3 4 5 6 7 8 9
// 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';
10
import 'package:test/fake.dart';
11 12 13 14 15

import '../../src/common.dart';

void main() {
  group('Signals', () {
16 17 18
    late Signals signals;
    late FakeProcessSignal fakeSignal;
    late ProcessSignal signalUnderTest;
19 20

    setUp(() {
21
      signals = Signals.test();
22 23
      fakeSignal = FakeProcessSignal();
      signalUnderTest = ProcessSignal(fakeSignal);
24 25
    });

26
    testWithoutContext('signal handler runs', () async {
27 28 29 30 31 32
      final Completer<void> completer = Completer<void>();
      signals.addHandler(signalUnderTest, (ProcessSignal s) {
        expect(s, signalUnderTest);
        completer.complete();
      });

33
      fakeSignal.controller.add(fakeSignal);
34 35 36
      await completer.future;
    });

37
    testWithoutContext('signal handlers run in order', () async {
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
      final Completer<void> completer = Completer<void>();

      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();
      });

53
      fakeSignal.controller.add(fakeSignal);
54 55 56
      await completer.future;
    });

57 58
    testWithoutContext('signal handlers do not cause concurrent modification errors when removing handlers in a signal callback', () async {
      final Completer<void> completer = Completer<void>();
59
      late Object token;
60 61 62 63 64 65 66 67 68 69 70 71
      Future<void> handle(ProcessSignal s) async {
        expect(s, signalUnderTest);
        expect(await signals.removeHandler(signalUnderTest, token), true);
        completer.complete();
      }

      token = signals.addHandler(signalUnderTest, handle);

      fakeSignal.controller.add(fakeSignal);
      await completer.future;
    });

72
    testWithoutContext('signal handler error goes on error stream', () async {
73
      final Exception exn = Exception('Error');
74
      signals.addHandler(signalUnderTest, (ProcessSignal s) async {
75
        throw exn;
76 77 78 79
      });

      final Completer<void> completer = Completer<void>();
      final List<Object> errList = <Object>[];
80 81 82 83 84 85
      final StreamSubscription<Object> errSub = signals.errors.listen(
        (Object err) {
          errList.add(err);
          completer.complete();
        },
      );
86

87
      fakeSignal.controller.add(fakeSignal);
88 89
      await completer.future;
      await errSub.cancel();
90
      expect(errList, contains(exn));
91 92
    });

93 94 95
    testWithoutContext('removed signal handler does not run', () async {
      final Object token = signals.addHandler(
        signalUnderTest,
96
        (ProcessSignal s) async {
97 98 99
          fail('Signal handler should have been removed.');
        },
      );
100 101 102 103

      await signals.removeHandler(signalUnderTest, token);

      final List<Object> errList = <Object>[];
104 105 106 107 108
      final StreamSubscription<Object> errSub = signals.errors.listen(
        (Object err) {
          errList.add(err);
        },
      );
109

110
      fakeSignal.controller.add(fakeSignal);
111 112 113 114 115

      await errSub.cancel();
      expect(errList, isEmpty);
    });

116
    testWithoutContext('non-removed signal handler still runs', () async {
117 118 119 120 121 122
      final Completer<void> completer = Completer<void>();
      signals.addHandler(signalUnderTest, (ProcessSignal s) {
        expect(s, signalUnderTest);
        completer.complete();
      });

123 124
      final Object token = signals.addHandler(
        signalUnderTest,
125
        (ProcessSignal s) async {
126 127 128
          fail('Signal handler should have been removed.');
        },
      );
129 130 131
      await signals.removeHandler(signalUnderTest, token);

      final List<Object> errList = <Object>[];
132 133 134 135 136
      final StreamSubscription<Object> errSub = signals.errors.listen(
        (Object err) {
          errList.add(err);
        },
      );
137

138
      fakeSignal.controller.add(fakeSignal);
139 140 141 142 143
      await completer.future;
      await errSub.cancel();
      expect(errList, isEmpty);
    });

144
    testWithoutContext('only handlers for the correct signal run', () async {
145
      final FakeProcessSignal mockSignal2 = FakeProcessSignal();
146 147 148 149 150 151 152 153
      final ProcessSignal otherSignal = ProcessSignal(mockSignal2);

      final Completer<void> completer = Completer<void>();
      signals.addHandler(signalUnderTest, (ProcessSignal s) {
        expect(s, signalUnderTest);
        completer.complete();
      });

154
      signals.addHandler(otherSignal, (ProcessSignal s) async {
155 156 157 158
        fail('Wrong signal!.');
      });

      final List<Object> errList = <Object>[];
159 160 161 162 163
      final StreamSubscription<Object> errSub = signals.errors.listen(
        (Object err) {
          errList.add(err);
        },
      );
164

165
      fakeSignal.controller.add(fakeSignal);
166 167 168
      await completer.future;
      await errSub.cancel();
      expect(errList, isEmpty);
169 170
    });

171 172 173 174
    testWithoutContext('all handlers for exiting signals are run before exit', () async {
      final Signals signals = Signals.test(
        exitSignals: <ProcessSignal>[signalUnderTest],
      );
175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201
      final Completer<void> completer = Completer<void>();
      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;
      });

202
      fakeSignal.controller.add(fakeSignal);
203
      await completer.future;
204 205 206 207
    });
  });
}

208 209 210 211 212 213
class FakeProcessSignal extends Fake implements io.ProcessSignal {
  final StreamController<io.ProcessSignal> controller = StreamController<io.ProcessSignal>();

  @override
  Stream<io.ProcessSignal> watch() => controller.stream;
}