process_test.dart 12.7 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1
// Copyright 2014 The Flutter Authors. All rights reserved.
2 3 4
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5
import 'package:flutter_tools/src/base/io.dart';
6
import 'package:flutter_tools/src/base/logger.dart';
7
import 'package:flutter_tools/src/base/platform.dart';
8
import 'package:flutter_tools/src/base/process.dart';
9
import 'package:flutter_tools/src/base/terminal.dart';
10

11
import '../../src/common.dart';
12
import '../../src/fake_process_manager.dart';
13
import '../../src/fakes.dart';
14

15
void main() {
16
  group('process exceptions', () {
17 18
    late FakeProcessManager fakeProcessManager;
    late ProcessUtils processUtils;
19 20

    setUp(() {
21
      fakeProcessManager = FakeProcessManager.empty();
22
      processUtils = ProcessUtils(
23
        processManager: fakeProcessManager,
24
        logger: BufferLogger.test(),
25
      );
26 27
    });

28
    testWithoutContext('runAsync throwOnError: exceptions should be ProcessException objects', () async {
29 30 31 32 33 34 35
      fakeProcessManager.addCommand(const FakeCommand(
        command: <String>[
          'false',
        ],
        exitCode: 1,
      ));

36
      expect(() async => processUtils.run(<String>['false'], throwOnError: true), throwsProcessException());
37
    });
38
  });
39

40
  group('shutdownHooks', () {
41
    testWithoutContext('runInExpectedOrder', () async {
42
      int i = 1;
43
      int? cleanup;
44

45
      final ShutdownHooks shutdownHooks = ShutdownHooks();
46 47

      shutdownHooks.addShutdownHook(() async {
48
        cleanup = i++;
49
      });
50

51
      await shutdownHooks.runShutdownHooks(BufferLogger.test());
52

53
      expect(cleanup, 1);
54 55
    });
  });
56

57
  group('output formatting', () {
58 59 60
    late FakeProcessManager processManager;
    late ProcessUtils processUtils;
    late BufferLogger logger;
61 62

    setUp(() {
63 64
      processManager = FakeProcessManager.empty();
      logger = BufferLogger.test();
65
      processUtils = ProcessUtils(
66 67
        processManager: processManager,
        logger: logger,
68
      );
69 70
    });

71
    testWithoutContext('Command output is not wrapped.', () async {
72
      final List<String> testString = <String>['0123456789' * 10];
73 74
      processManager.addCommand(FakeCommand(
        command: const <String>['command'],
75 76
        stdout: testString.join(),
        stderr: testString.join(),
77 78
      ));

79
      await processUtils.stream(<String>['command']);
80 81 82

      expect(logger.statusText, equals('${testString[0]}\n'));
      expect(logger.errorText, equals('${testString[0]}\n'));
83
    });
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101

    testWithoutContext('Command output is filtered by mapFunction', () async {
      processManager.addCommand(const FakeCommand(
        command: <String>['command'],
        stdout: 'match\nno match',
        stderr: 'match\nno match',
      ));

      await processUtils.stream(<String>['command'], mapFunction: (String line) {
        if (line == 'match') {
          return line;
        }
        return null;
      });

      expect(logger.statusText, equals('match\n'));
      expect(logger.errorText, equals('match\n'));
    });
102
  });
103

104
  group('run', () {
105 106
    late FakeProcessManager fakeProcessManager;
    late ProcessUtils processUtils;
107 108

    setUp(() {
109
      fakeProcessManager = FakeProcessManager.empty();
110
      processUtils = ProcessUtils(
111
        processManager: fakeProcessManager,
112
        logger: BufferLogger.test(),
113
      );
114 115
    });

116
    testWithoutContext(' succeeds on success', () async {
117 118 119 120 121
      fakeProcessManager.addCommand(const FakeCommand(
        command: <String>[
          'whoohoo',
        ],
      ));
122 123 124
      expect((await processUtils.run(<String>['whoohoo'])).exitCode, 0);
    });

125
    testWithoutContext(' fails on failure', () async {
126 127 128 129 130 131
      fakeProcessManager.addCommand(const FakeCommand(
        command: <String>[
          'boohoo',
        ],
        exitCode: 1,
      ));
132 133 134
      expect((await processUtils.run(<String>['boohoo'])).exitCode, 1);
    });

135
    testWithoutContext(' throws on failure with throwOnError', () async {
136 137 138 139 140 141
      fakeProcessManager.addCommand(const FakeCommand(
        command: <String>[
          'kaboom',
        ],
        exitCode: 1,
      ));
142
      expect(() => processUtils.run(<String>['kaboom'], throwOnError: true), throwsProcessException());
143 144
    });

145
    testWithoutContext(' does not throw on allowed Failures', () async {
146 147 148 149 150 151
      fakeProcessManager.addCommand(const FakeCommand(
        command: <String>[
          'kaboom',
        ],
        exitCode: 1,
      ));
152 153 154 155
      expect(
        (await processUtils.run(
          <String>['kaboom'],
          throwOnError: true,
156
          allowedFailures: (int c) => c == 1,
157
        )).exitCode,
158 159
        1,
      );
160 161
    });

162
    testWithoutContext(' throws on disallowed failure', () async {
163 164 165 166 167 168
      fakeProcessManager.addCommand(const FakeCommand(
        command: <String>[
          'kaboom',
        ],
        exitCode: 2,
      ));
169 170 171 172
      expect(
        () => processUtils.run(
          <String>['kaboom'],
          throwOnError: true,
173
          allowedFailures: (int c) => c == 1,
174
        ),
175
        throwsProcessException(),
176
      );
177
    });
178
  });
179 180

  group('runSync', () {
181 182 183
    late FakeProcessManager fakeProcessManager;
    late ProcessUtils processUtils;
    late BufferLogger testLogger;
184 185

    setUp(() {
186
      fakeProcessManager = FakeProcessManager.empty();
187 188
      testLogger = BufferLogger(
        terminal: AnsiTerminal(
189
          stdio: FakeStdio(),
190
          platform: FakePlatform(),
191 192 193 194
        ),
        outputPreferences: OutputPreferences(wrapText: true, wrapColumn: 40),
      );
      processUtils = ProcessUtils(
195
        processManager: fakeProcessManager,
196 197
        logger: testLogger,
      );
198 199
    });

200
    testWithoutContext(' succeeds on success', () async {
201 202 203 204 205
      fakeProcessManager.addCommand(const FakeCommand(
        command: <String>[
          'whoohoo',
        ],
      ));
206 207 208
      expect(processUtils.runSync(<String>['whoohoo']).exitCode, 0);
    });

209
    testWithoutContext(' fails on failure', () async {
210 211 212 213 214 215
      fakeProcessManager.addCommand(const FakeCommand(
        command: <String>[
          'boohoo',
        ],
        exitCode: 1,
      ));
216 217 218
      expect(processUtils.runSync(<String>['boohoo']).exitCode, 1);
    });

219 220
    testWithoutContext('throws on failure with throwOnError', () async {
      const String stderr = 'Something went wrong.';
221 222 223 224 225 226 227
      fakeProcessManager.addCommand(const FakeCommand(
        command: <String>[
          'kaboom',
        ],
        exitCode: 1,
        stderr: stderr,
      ));
228 229 230 231 232 233 234 235
      expect(
        () => processUtils.runSync(<String>['kaboom'], throwOnError: true),
        throwsA(isA<ProcessException>().having(
          (ProcessException error) => error.message,
          'message',
          isNot(contains(stderr)),
        )),
      );
236 237 238 239
    });

    testWithoutContext('throws with stderr in exception on failure with verboseExceptions', () async {
      const String stderr = 'Something went wrong.';
240 241 242 243 244 245 246
      fakeProcessManager.addCommand(const FakeCommand(
        command: <String>[
          'verybad',
        ],
        exitCode: 1,
        stderr: stderr,
      ));
247 248 249 250 251 252 253
      expect(
        () => processUtils.runSync(
          <String>['verybad'],
          throwOnError: true,
          verboseExceptions: true,
        ),
        throwsProcessException(message: stderr),
254 255 256
      );
    });

257
    testWithoutContext(' does not throw on allowed Failures', () async {
258 259 260 261 262 263
      fakeProcessManager.addCommand(const FakeCommand(
        command: <String>[
          'kaboom',
        ],
        exitCode: 1,
      ));
264 265 266 267
      expect(
        processUtils.runSync(
          <String>['kaboom'],
          throwOnError: true,
268
          allowedFailures: (int c) => c == 1,
269 270 271 272
        ).exitCode,
        1);
    });

273
    testWithoutContext(' throws on disallowed failure', () async {
274 275 276 277 278 279
      fakeProcessManager.addCommand(const FakeCommand(
        command: <String>[
          'kaboom',
        ],
        exitCode: 2,
      ));
280 281 282 283
      expect(
        () => processUtils.runSync(
          <String>['kaboom'],
          throwOnError: true,
284
          allowedFailures: (int c) => c == 1,
285
        ),
286 287
        throwsProcessException(),
      );
288 289
    });

290
    testWithoutContext(' prints stdout and stderr to trace on success', () async {
291 292 293 294 295 296 297
      fakeProcessManager.addCommand(const FakeCommand(
        command: <String>[
          'whoohoo',
        ],
        stdout: 'stdout',
        stderr: 'stderr',
      ));
298 299 300 301 302
      expect(processUtils.runSync(<String>['whoohoo']).exitCode, 0);
      expect(testLogger.traceText, contains('stdout'));
      expect(testLogger.traceText, contains('stderr'));
    });

303
    testWithoutContext(' prints stdout to status and stderr to error on failure with throwOnError', () async {
304 305 306 307 308 309 310 311
      fakeProcessManager.addCommand(const FakeCommand(
        command: <String>[
          'kaboom',
        ],
        exitCode: 1,
        stdout: 'stdout',
        stderr: 'stderr',
      ));
312
      expect(() => processUtils.runSync(<String>['kaboom'], throwOnError: true), throwsProcessException());
313 314 315 316
      expect(testLogger.statusText, contains('stdout'));
      expect(testLogger.errorText, contains('stderr'));
    });

317
    testWithoutContext(' does not print stdout with hideStdout', () async {
318 319 320 321 322 323 324
      fakeProcessManager.addCommand(const FakeCommand(
        command: <String>[
          'whoohoo',
        ],
        stdout: 'stdout',
        stderr: 'stderr',
      ));
325 326 327 328 329 330 331
      expect(processUtils.runSync(<String>['whoohoo'], hideStdout: true).exitCode, 0);
      expect(testLogger.traceText.contains('stdout'), isFalse);
      expect(testLogger.traceText, contains('stderr'));
    });
  });

  group('exitsHappySync', () {
332 333
    late FakeProcessManager processManager;
    late ProcessUtils processUtils;
334 335

    setUp(() {
336
      processManager = FakeProcessManager.empty();
337
      processUtils = ProcessUtils(
338
        processManager: processManager,
339
        logger: BufferLogger.test(),
340
      );
341 342
    });

343 344 345 346 347
    testWithoutContext('succeeds on success', () async {
      processManager.addCommand(const FakeCommand(
        command: <String>['whoohoo'],
      ));

348 349 350
      expect(processUtils.exitsHappySync(<String>['whoohoo']), isTrue);
    });

351 352 353 354 355 356
    testWithoutContext('fails on failure', () async {
      processManager.addCommand(const FakeCommand(
        command: <String>['boohoo'],
        exitCode: 1,
      ));

357 358
      expect(processUtils.exitsHappySync(<String>['boohoo']), isFalse);
    });
359 360

    testWithoutContext('catches Exception and returns false', () {
361 362 363 364 365
      processManager.addCommand(const FakeCommand(
        command: <String>['boohoo'],
        exception: ProcessException('Process failed', <String>[]),
      ));

366 367 368
      expect(processUtils.exitsHappySync(<String>['boohoo']), isFalse);
    });

369
    testWithoutContext('does not throw Exception and returns false if binary cannot run', () {
370 371
      processManager.excludedExecutables.add('nonesuch');

372
      expect(processUtils.exitsHappySync(<String>['nonesuch']), isFalse);
373 374 375
    });

    testWithoutContext('does not catch ArgumentError', () async {
376 377 378 379 380
      processManager.addCommand(FakeCommand(
        command: const <String>['invalid'],
        exception: ArgumentError('Bad input'),
      ));

381 382 383 384
      expect(
        () => processUtils.exitsHappySync(<String>['invalid']),
        throwsArgumentError,
      );
385
    });
386 387 388
  });

  group('exitsHappy', () {
389 390
    late FakeProcessManager processManager;
    late ProcessUtils processUtils;
391 392

    setUp(() {
393
      processManager = FakeProcessManager.empty();
394
      processUtils = ProcessUtils(
395
        processManager: processManager,
396
        logger: BufferLogger.test(),
397
      );
398 399
    });

400
    testWithoutContext('succeeds on success', () async {
401 402 403 404
      processManager.addCommand(const FakeCommand(
        command: <String>['whoohoo']
      ));

405 406 407
      expect(await processUtils.exitsHappy(<String>['whoohoo']), isTrue);
    });

408
    testWithoutContext('fails on failure', () async {
409 410 411 412 413
      processManager.addCommand(const FakeCommand(
        command: <String>['boohoo'],
        exitCode: 1,
      ));

414 415
      expect(await processUtils.exitsHappy(<String>['boohoo']), isFalse);
    });
416 417

    testWithoutContext('catches Exception and returns false', () async {
418 419 420 421 422
      processManager.addCommand(const FakeCommand(
        command: <String>['boohoo'],
        exception: ProcessException('Process failed', <String>[])
      ));

423 424 425
      expect(await processUtils.exitsHappy(<String>['boohoo']), isFalse);
    });

426
    testWithoutContext('does not throw Exception and returns false if binary cannot run', () async {
427 428
      processManager.excludedExecutables.add('nonesuch');

429
      expect(await processUtils.exitsHappy(<String>['nonesuch']), isFalse);
430 431 432
    });

    testWithoutContext('does not catch ArgumentError', () async {
433 434 435 436 437
      processManager.addCommand(FakeCommand(
        command: const <String>['invalid'],
        exception: ArgumentError('Bad input')
      ));

438
      expect(
439
        () async => processUtils.exitsHappy(<String>['invalid']),
440 441
        throwsArgumentError,
      );
442
    });
443
  });
444
}