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

import 'dart:async';
6 7
import 'dart:convert';
import 'dart:io';
8 9 10 11

import 'package:file/memory.dart';
import 'package:flutter_tools/src/base/context.dart';
import 'package:flutter_tools/src/base/file_system.dart';
12
import 'package:flutter_tools/src/base/io.dart';
13
import 'package:flutter_tools/src/base/logger.dart';
14
import 'package:flutter_tools/src/base/os.dart';
Dan Field's avatar
Dan Field committed
15
import 'package:flutter_tools/src/base/process.dart';
16

17
import 'package:flutter_tools/src/base/signals.dart';
18
import 'package:flutter_tools/src/base/terminal.dart';
19 20
import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/context_runner.dart';
21
import 'package:flutter_tools/src/dart/pub.dart';
22
import 'package:flutter_tools/src/features.dart';
23
import 'package:flutter_tools/src/reporting/reporting.dart';
24
import 'package:flutter_tools/src/version.dart';
25
import 'package:flutter_tools/src/globals.dart' as globals;
26
import 'package:meta/meta.dart';
27
import 'package:process/process.dart';
28

29
import 'common.dart' as tester;
30
import 'context.dart';
31 32
import 'fake_process_manager.dart';
import 'throwing_pub.dart';
33

34 35
export 'package:flutter_tools/src/base/context.dart' show Generator;

36 37
// A default value should be provided if the vast majority of tests should use
// this provider. For example, [BufferLogger], [MemoryFileSystem].
38
final Map<Type, Generator> _testbedDefaults = <Type, Generator>{
39
  // Keeps tests fast by avoiding the actual file system.
40
  FileSystem: () => MemoryFileSystem(style: globals.platform.isWindows ? FileSystemStyle.windows : FileSystemStyle.posix),
41
  ProcessManager: () => FakeProcessManager.any(),
42 43 44 45
  Logger: () => BufferLogger(
    terminal: AnsiTerminal(stdio: globals.stdio, platform: globals.platform),  // Danger, using real stdio.
    outputPreferences: OutputPreferences.test(),
  ), // Allows reading logs and prevents stdout.
46
  OperatingSystemUtils: () => FakeOperatingSystemUtils(),
47
  OutputPreferences: () => OutputPreferences.test(), // configures BufferLogger to avoid color codes.
48
  Usage: () => NoOpUsage(), // prevent addition of analytics from burdening test mocks
49
  FlutterVersion: () => FakeFlutterVersion(), // prevent requirement to mock git for test runner.
50
  Signals: () => FakeSignals(),  // prevent registering actual signal handlers.
51
  Pub: () => ThrowingPub(), // prevent accidental invocations of pub.
52 53 54 55 56 57 58 59 60
};

/// Manages interaction with the tool injection and runner system.
///
/// The Testbed automatically injects reasonable defaults through the context
/// DI system such as a [BufferLogger] and a [MemoryFileSytem].
///
/// Example:
///
61
/// Testing that a filesystem operation works as expected:
62 63 64 65 66 67 68
///
///     void main() {
///       group('Example', () {
///         Testbed testbed;
///
///         setUp(() {
///           testbed = Testbed(setUp: () {
69
///             globals.fs.file('foo').createSync()
70 71 72
///           });
///         })
///
73
///         test('Can delete a file', () => testbed.run(() {
74 75 76
///           expect(globals.fs.file('foo').existsSync(), true);
///           globals.fs.file('foo').deleteSync();
///           expect(globals.fs.file('foo').existsSync(), false);
77 78 79 80 81 82 83 84 85 86 87
///         }));
///       });
///     }
///
/// For a more detailed example, see the code in test_compiler_test.dart.
class Testbed {
  /// Creates a new [TestBed]
  ///
  /// `overrides` provides more overrides in addition to the test defaults.
  /// `setup` may be provided to apply mocks within the tool managed zone,
  /// including any specified overrides.
88
  Testbed({FutureOr<void> Function() setup, Map<Type, Generator> overrides})
89 90
      : _setup = setup,
        _overrides = overrides;
91

92
  final FutureOr<void> Function() _setup;
93 94
  final Map<Type, Generator> _overrides;

95 96 97 98 99 100 101 102 103 104
  /// Runs the `test` within a tool zone.
  ///
  /// Unlike [run], this sets up a test group on its own.
  @isTest
  void test<T>(String name, FutureOr<T> Function() test, {Map<Type, Generator> overrides}) {
    tester.test(name, () {
      return run(test, overrides: overrides);
    });
  }

105
  /// Runs `test` within a tool zone.
106 107 108
  ///
  /// `overrides` may be used to provide new context values for the single test
  /// case or override any context values from the setup.
109
  Future<T> run<T>(FutureOr<T> Function() test, {Map<Type, Generator> overrides}) {
110 111 112 113 114 115 116
    final Map<Type, Generator> testOverrides = <Type, Generator>{
      ..._testbedDefaults,
      // Add the initial setUp overrides
      ...?_overrides,
      // Add the test-specific overrides
      ...?overrides,
    };
Dan Field's avatar
Dan Field committed
117 118 119
    if (testOverrides.containsKey(ProcessUtils)) {
      throw StateError('Do not inject ProcessUtils for testing, use ProcessManager instead.');
    }
120 121
    // Cache the original flutter root to restore after the test case.
    final String originalFlutterRoot = Cache.flutterRoot;
122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
    // Track pending timers to verify that they were correctly cleaned up.
    final Map<Timer, StackTrace> timers = <Timer, StackTrace>{};

    return HttpOverrides.runZoned(() {
      return runInContext<T>(() {
        return context.run<T>(
          name: 'testbed',
          overrides: testOverrides,
          zoneSpecification: ZoneSpecification(
            createTimer: (Zone self, ZoneDelegate parent, Zone zone, Duration duration, void Function() timer) {
              final Timer result = parent.createTimer(zone, duration, timer);
              timers[result] = StackTrace.current;
              return result;
            },
            createPeriodicTimer: (Zone self, ZoneDelegate parent, Zone zone, Duration period, void Function(Timer) timer) {
              final Timer result = parent.createPeriodicTimer(zone, period, timer);
              timers[result] = StackTrace.current;
              return result;
140
            },
141 142 143 144 145 146 147 148
          ),
          body: () async {
            Cache.flutterRoot = '';
            if (_setup != null) {
              await _setup();
            }
            await test();
            Cache.flutterRoot = originalFlutterRoot;
149
            for (final MapEntry<Timer, StackTrace> entry in timers.entries) {
150 151 152 153 154 155 156 157
              if (entry.key.isActive) {
                throw StateError('A Timer was active at the end of a test: ${entry.value}');
              }
            }
            return null;
          });
      });
    }, createHttpClient: (SecurityContext c) => FakeHttpClient());
158
  }
159
}
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180

/// A no-op implementation of [Usage] for testing.
class NoOpUsage implements Usage {
  @override
  bool enabled = false;

  @override
  bool suppressAnalytics = true;

  @override
  String get clientId => 'test';

  @override
  Future<void> ensureAnalyticsSent() {
    return null;
  }

  @override
  bool get isFirstRun => false;

  @override
181
  Stream<Map<String, Object>> get onSend => const Stream<Map<String, Object>>.empty();
182 183 184 185 186 187 188 189

  @override
  void printWelcome() {}

  @override
  void sendCommand(String command, {Map<String, String> parameters}) {}

  @override
190 191 192 193 194
  void sendEvent(String category, String parameter, {
    String label,
    int value,
    Map<String, String> parameters,
  }) {}
195 196

  @override
197
  void sendException(dynamic exception) {}
198 199

  @override
200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219
  void sendTiming(String category, String variableName, Duration duration, { String label }) {}
}

class FakeHttpClient implements HttpClient {
  @override
  bool autoUncompress;

  @override
  Duration connectionTimeout;

  @override
  Duration idleTimeout;

  @override
  int maxConnectionsPerHost;

  @override
  String userAgent;

  @override
220
  void addCredentials(Uri url, String realm, HttpClientCredentials credentials) {}
221 222

  @override
223
  void addProxyCredentials(String host, int port, String realm, HttpClientCredentials credentials) {}
224 225

  @override
226
  set authenticate(Future<bool> Function(Uri url, String scheme, String realm) f) {}
227 228

  @override
229
  set authenticateProxy(Future<bool> Function(String host, int port, String scheme, String realm) f) {}
230 231

  @override
232
  set badCertificateCallback(bool Function(X509Certificate cert, String host, int port) callback) {}
233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360

  @override
  void close({bool force = false}) {}

  @override
  Future<HttpClientRequest> delete(String host, int port, String path) async {
    return FakeHttpClientRequest();
  }

  @override
  Future<HttpClientRequest> deleteUrl(Uri url) async {
    return FakeHttpClientRequest();
  }

  @override
  set findProxy(String Function(Uri url) f) {}

  @override
  Future<HttpClientRequest> get(String host, int port, String path) async {
    return FakeHttpClientRequest();
  }

  @override
  Future<HttpClientRequest> getUrl(Uri url) async {
    return FakeHttpClientRequest();
  }

  @override
  Future<HttpClientRequest> head(String host, int port, String path) async {
    return FakeHttpClientRequest();
  }

  @override
  Future<HttpClientRequest> headUrl(Uri url) async {
    return FakeHttpClientRequest();
  }

  @override
  Future<HttpClientRequest> open(String method, String host, int port, String path) async {
    return FakeHttpClientRequest();
  }

  @override
  Future<HttpClientRequest> openUrl(String method, Uri url) async {
    return FakeHttpClientRequest();
  }

  @override
  Future<HttpClientRequest> patch(String host, int port, String path) async {
    return FakeHttpClientRequest();
  }

  @override
  Future<HttpClientRequest> patchUrl(Uri url) async {
    return FakeHttpClientRequest();
  }

  @override
  Future<HttpClientRequest> post(String host, int port, String path) async {
    return FakeHttpClientRequest();
  }

  @override
  Future<HttpClientRequest> postUrl(Uri url) async {
    return FakeHttpClientRequest();
  }

  @override
  Future<HttpClientRequest> put(String host, int port, String path) async {
    return FakeHttpClientRequest();
  }

  @override
  Future<HttpClientRequest> putUrl(Uri url) async {
    return FakeHttpClientRequest();
  }
}

class FakeHttpClientRequest implements HttpClientRequest {
  FakeHttpClientRequest();

  @override
  bool bufferOutput;

  @override
  int contentLength;

  @override
  Encoding encoding;

  @override
  bool followRedirects;

  @override
  int maxRedirects;

  @override
  bool persistentConnection;

  @override
  void add(List<int> data) {}

  @override
  void addError(Object error, [StackTrace stackTrace]) {}

  @override
  Future<void> addStream(Stream<List<int>> stream) async {}

  @override
  Future<HttpClientResponse> close() async {
    return FakeHttpClientResponse();
  }

  @override
  HttpConnectionInfo get connectionInfo => null;

  @override
  List<Cookie> get cookies => <Cookie>[];

  @override
  Future<HttpClientResponse> get done => null;

  @override
  Future<void> flush() {
    return Future<void>.value();
  }

  @override
361
  HttpHeaders get headers => FakeHttpHeaders();
362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379

  @override
  String get method => null;

  @override
  Uri get uri => null;

  @override
  void write(Object obj) {}

  @override
  void writeAll(Iterable<Object> objects, [String separator = '']) {}

  @override
  void writeCharCode(int charCode) {}

  @override
  void writeln([Object obj = '']) {}
380

381
  // TODO(zichangguo): remove the ignore after the change in dart:io lands.
382
  @override
383
  // ignore: override_on_non_overriding_member
384
  void abort([Object exception, StackTrace stackTrace]) {}
385 386
}

387
class FakeHttpClientResponse implements HttpClientResponse {
388
  final Stream<List<int>> _delegate = Stream<List<int>>.fromIterable(const Iterable<List<int>>.empty());
389 390

  @override
391
  final HttpHeaders headers = FakeHttpHeaders();
392 393

  @override
394
  X509Certificate get certificate => null;
395 396 397 398 399 400 401 402

  @override
  HttpConnectionInfo get connectionInfo => null;

  @override
  int get contentLength => 0;

  @override
403 404 405
  HttpClientResponseCompressionState get compressionState {
    return HttpClientResponseCompressionState.decompressed;
  }
406 407

  @override
408
  List<Cookie> get cookies => null;
409 410

  @override
411 412 413
  Future<Socket> detachSocket() {
    return Future<Socket>.error(UnsupportedError('Mocked response'));
  }
414 415

  @override
416
  bool get isRedirect => false;
417 418

  @override
419 420
  StreamSubscription<List<int>> listen(void Function(List<int> event) onData, { Function onError, void Function() onDone, bool cancelOnError }) {
    return const Stream<List<int>>.empty().listen(onData, onError: onError, onDone: onDone, cancelOnError: cancelOnError);
421 422 423
  }

  @override
424
  bool get persistentConnection => null;
425 426 427 428 429

  @override
  String get reasonPhrase => null;

  @override
430 431 432 433 434 435 436 437 438 439 440
  Future<HttpClientResponse> redirect([ String method, Uri url, bool followLoops ]) {
    return Future<HttpClientResponse>.error(UnsupportedError('Mocked response'));
  }

  @override
  List<RedirectInfo> get redirects => <RedirectInfo>[];

  @override
  int get statusCode => 400;

  @override
441
  Future<bool> any(bool Function(List<int> element) test) {
442 443 444 445
    return _delegate.any(test);
  }

  @override
446 447 448
  Stream<List<int>> asBroadcastStream({
    void Function(StreamSubscription<List<int>> subscription) onListen,
    void Function(StreamSubscription<List<int>> subscription) onCancel,
449 450 451 452 453
  }) {
    return _delegate.asBroadcastStream(onListen: onListen, onCancel: onCancel);
  }

  @override
454
  Stream<E> asyncExpand<E>(Stream<E> Function(List<int> event) convert) {
455 456 457 458
    return _delegate.asyncExpand<E>(convert);
  }

  @override
459
  Stream<E> asyncMap<E>(FutureOr<E> Function(List<int> event) convert) {
460 461 462 463 464 465 466 467 468 469 470 471 472 473
    return _delegate.asyncMap<E>(convert);
  }

  @override
  Stream<R> cast<R>() {
    return _delegate.cast<R>();
  }

  @override
  Future<bool> contains(Object needle) {
    return _delegate.contains(needle);
  }

  @override
474
  Stream<List<int>> distinct([bool Function(List<int> previous, List<int> next) equals]) {
475 476 477 478 479 480
    return _delegate.distinct(equals);
  }

  @override
  Future<E> drain<E>([E futureValue]) {
    return _delegate.drain<E>(futureValue);
481 482 483
  }

  @override
484
  Future<List<int>> elementAt(int index) {
485 486 487 488
    return _delegate.elementAt(index);
  }

  @override
489
  Future<bool> every(bool Function(List<int> element) test) {
490 491 492 493
    return _delegate.every(test);
  }

  @override
494
  Stream<S> expand<S>(Iterable<S> Function(List<int> element) convert) {
495 496 497 498
    return _delegate.expand(convert);
  }

  @override
499
  Future<List<int>> get first => _delegate.first;
500 501

  @override
502 503
  Future<List<int>> firstWhere(
    bool Function(List<int> element) test, {
504 505 506 507 508 509
    List<int> Function() orElse,
  }) {
    return _delegate.firstWhere(test, orElse: orElse);
  }

  @override
510
  Future<S> fold<S>(S initialValue, S Function(S previous, List<int> element) combine) {
511 512 513 514
    return _delegate.fold<S>(initialValue, combine);
  }

  @override
515
  Future<dynamic> forEach(void Function(List<int> element) action) {
516 517 518 519
    return _delegate.forEach(action);
  }

  @override
520
  Stream<List<int>> handleError(
521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538
    Function onError, {
    bool Function(dynamic error) test,
  }) {
    return _delegate.handleError(onError, test: test);
  }

  @override
  bool get isBroadcast => _delegate.isBroadcast;

  @override
  Future<bool> get isEmpty => _delegate.isEmpty;

  @override
  Future<String> join([String separator = '']) {
    return _delegate.join(separator);
  }

  @override
539
  Future<List<int>> get last => _delegate.last;
540 541

  @override
542 543
  Future<List<int>> lastWhere(
    bool Function(List<int> element) test, {
544 545 546 547 548 549 550 551 552
    List<int> Function() orElse,
  }) {
    return _delegate.lastWhere(test, orElse: orElse);
  }

  @override
  Future<int> get length => _delegate.length;

  @override
553
  Stream<S> map<S>(S Function(List<int> event) convert) {
554 555 556 557 558 559 560 561 562
    return _delegate.map<S>(convert);
  }

  @override
  Future<dynamic> pipe(StreamConsumer<List<int>> streamConsumer) {
    return _delegate.pipe(streamConsumer);
  }

  @override
563
  Future<List<int>> reduce(List<int> Function(List<int> previous, List<int> element) combine) {
564 565 566 567
    return _delegate.reduce(combine);
  }

  @override
568
  Future<List<int>> get single => _delegate.single;
569 570

  @override
571
  Future<List<int>> singleWhere(bool Function(List<int> element) test, {List<int> Function() orElse}) {
572 573 574 575
    return _delegate.singleWhere(test, orElse: orElse);
  }

  @override
576
  Stream<List<int>> skip(int count) {
577 578 579 580
    return _delegate.skip(count);
  }

  @override
581
  Stream<List<int>> skipWhile(bool Function(List<int> element) test) {
582 583 584 585
    return _delegate.skipWhile(test);
  }

  @override
586
  Stream<List<int>> take(int count) {
587 588 589 590
    return _delegate.take(count);
  }

  @override
591
  Stream<List<int>> takeWhile(bool Function(List<int> element) test) {
592 593 594 595
    return _delegate.takeWhile(test);
  }

  @override
596
  Stream<List<int>> timeout(
597
    Duration timeLimit, {
598
    void Function(EventSink<List<int>> sink) onTimeout,
599 600 601 602 603
  }) {
    return _delegate.timeout(timeLimit, onTimeout: onTimeout);
  }

  @override
604
  Future<List<List<int>>> toList() {
605 606 607 608
    return _delegate.toList();
  }

  @override
609
  Future<Set<List<int>>> toSet() {
610 611 612 613 614 615 616 617 618
    return _delegate.toSet();
  }

  @override
  Stream<S> transform<S>(StreamTransformer<List<int>, S> streamTransformer) {
    return _delegate.transform<S>(streamTransformer);
  }

  @override
619
  Stream<List<int>> where(bool Function(List<int> event) test) {
620 621 622 623 624 625 626 627 628 629
    return _delegate.where(test);
  }
}

/// A fake [HttpHeaders] that ignores all writes.
class FakeHttpHeaders extends HttpHeaders {
  @override
  List<String> operator [](String name) => <String>[];

  @override
630
  void add(String name, Object value, {bool preserveHeaderCase = false}) { }
631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647

  @override
  void clear() { }

  @override
  void forEach(void Function(String name, List<String> values) f) { }

  @override
  void noFolding(String name) { }

  @override
  void remove(String name, Object value) { }

  @override
  void removeAll(String name) { }

  @override
648
  void set(String name, Object value, {bool preserveHeaderCase = false}) { }
649 650

  @override
651
  String value(String name) => null;
652 653 654
}

class FakeFlutterVersion implements FlutterVersion {
655 656 657
  @override
  void fetchTagsAndUpdate() {  }

658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698
  @override
  String get channel => 'master';

  @override
  Future<void> checkFlutterVersionFreshness() async { }

  @override
  bool checkRevisionAncestry({String tentativeDescendantRevision, String tentativeAncestorRevision}) {
    throw UnimplementedError();
  }

  @override
  String get dartSdkVersion => '12';

  @override
  String get engineRevision => '42.2';

  @override
  String get engineRevisionShort => '42';

  @override
  Future<void> ensureVersionFile() async { }

  @override
  String get frameworkAge => null;

  @override
  String get frameworkCommitDate => null;

  @override
  String get frameworkDate => null;

  @override
  String get frameworkRevision => null;

  @override
  String get frameworkRevisionShort => null;

  @override
  String get frameworkVersion => null;

699 700 701
  @override
  GitTagVersion get gitTagVersion => null;

702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722
  @override
  String getBranchName({bool redactUnknownBranches = false}) {
    return 'master';
  }

  @override
  String getVersionString({bool redactUnknownBranches = false}) {
    return 'v0.0.0';
  }

  @override
  bool get isMaster => true;

  @override
  String get repositoryUrl => null;

  @override
  Map<String, Object> toJson() {
    return null;
  }
}
723 724 725 726 727 728 729 730 731

// A test implementation of [FeatureFlags] that allows enabling without reading
// config. If not otherwise specified, all values default to false.
class TestFeatureFlags implements FeatureFlags {
  TestFeatureFlags({
    this.isLinuxEnabled = false,
    this.isMacOSEnabled = false,
    this.isWebEnabled = false,
    this.isWindowsEnabled = false,
732
    this.isSingleWidgetReloadEnabled = false,
733 734
    this.isAndroidEnabled = true,
    this.isIOSEnabled = true,
735
    this.isFuchsiaEnabled = false,
736 737 738 739 740 741 742 743 744 745 746 747 748
});

  @override
  final bool isLinuxEnabled;

  @override
  final bool isMacOSEnabled;

  @override
  final bool isWebEnabled;

  @override
  final bool isWindowsEnabled;
749

750 751 752
  @override
  final bool isSingleWidgetReloadEnabled;

753 754 755 756 757 758 759 760 761
  @override
  final bool isAndroidEnabled;

  @override
  final bool isIOSEnabled;

  @override
  final bool isFuchsiaEnabled;

762 763 764 765 766 767 768 769 770 771 772
  @override
  bool isEnabled(Feature feature) {
    switch (feature) {
      case flutterWebFeature:
        return isWebEnabled;
      case flutterLinuxDesktopFeature:
        return isLinuxEnabled;
      case flutterMacOSDesktopFeature:
        return isMacOSEnabled;
      case flutterWindowsDesktopFeature:
        return isWindowsEnabled;
773 774 775 776 777 778 779 780
      case singleWidgetReload:
        return isSingleWidgetReloadEnabled;
      case flutterAndroidFeature:
        return isAndroidEnabled;
      case flutterIOSFeature:
        return isIOSEnabled;
      case flutterFuchsiaFeature:
        return isFuchsiaEnabled;
781 782 783
    }
    return false;
  }
784
}
785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833

class DelegateLogger implements Logger {
  DelegateLogger(this.delegate);

  final Logger delegate;
  Status status;

  @override
  bool get quiet => delegate.quiet;

  @override
  set quiet(bool value) => delegate.quiet;

  @override
  bool get hasTerminal => delegate.hasTerminal;

  @override
  bool get isVerbose => delegate.isVerbose;

  @override
  void printError(String message, {StackTrace stackTrace, bool emphasis, TerminalColor color, int indent, int hangingIndent, bool wrap}) {
    delegate.printError(
      message,
      stackTrace: stackTrace,
      emphasis: emphasis,
      color: color,
      indent: indent,
      hangingIndent: hangingIndent,
      wrap: wrap,
    );
  }

  @override
  void printStatus(String message, {bool emphasis, TerminalColor color, bool newline, int indent, int hangingIndent, bool wrap}) {
    delegate.printStatus(message,
      emphasis: emphasis,
      color: color,
      indent: indent,
      hangingIndent: hangingIndent,
      wrap: wrap,
    );
  }

  @override
  void printTrace(String message) {
    delegate.printTrace(message);
  }

  @override
834 835
  void sendEvent(String name, [Map<String, dynamic> args]) {
    delegate.sendEvent(name, args);
836 837 838 839 840 841 842 843 844
  }

  @override
  Status startProgress(String message, {Duration timeout, String progressId, bool multilineOutput = false, int progressIndicatorPadding = kDefaultStatusPadding}) {
    return status;
  }

  @override
  bool get supportsColor => delegate.supportsColor;
845 846 847

  @override
  void clear() => delegate.clear();
848
}
849 850 851 852 853 854

/// An implementation of the Cache which does not download or require locking.
class FakeCache implements Cache {
  @override
  bool includeAllPlatforms;

855 856 857
  @override
  Set<String> platformOverrideArtifacts;

858 859 860 861 862 863 864 865 866 867 868
  @override
  bool useUnsignedMacBinaries;

  @override
  Future<bool> areRemoteArtifactsAvailable({String engineVersion, bool includeAllPlatforms = true}) async {
    return true;
  }

  @override
  String get dartSdkVersion => null;

869 870 871
  @override
  String get storageBaseUrl => null;

872
  @override
873
  MapEntry<String, String> get dyLdLibEntry => const MapEntry<String, String>('DYLD_LIBRARY_PATH', '');
874 875 876 877 878 879

  @override
  String get engineRevision => null;

  @override
  Directory getArtifactDirectory(String name) {
880
    return globals.fs.currentDirectory;
881 882 883 884
  }

  @override
  Directory getCacheArtifacts() {
885
    return globals.fs.currentDirectory;
886 887 888 889
  }

  @override
  Directory getCacheDir(String name) {
890
    return globals.fs.currentDirectory;
891 892 893 894
  }

  @override
  Directory getDownloadDir() {
895
    return globals.fs.currentDirectory;
896 897 898 899
  }

  @override
  Directory getRoot() {
900
    return globals.fs.currentDirectory;
901 902
  }

903 904
  @override
  File getLicenseFile() {
905
    return globals.fs.currentDirectory.childFile('LICENSE');
906 907
  }

908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924
  @override
  File getStampFileFor(String artifactName) {
    throw UnsupportedError('Not supported in the fake Cache');
  }

  @override
  String getStampFor(String artifactName) {
    throw UnsupportedError('Not supported in the fake Cache');
  }

  @override
  String getVersionFor(String artifactName) {
    throw UnsupportedError('Not supported in the fake Cache');
  }

  @override
  Directory getWebSdkDirectory() {
925
    return globals.fs.currentDirectory;
926 927 928 929 930 931 932 933
  }

  @override
  bool isOlderThanToolsStamp(FileSystemEntity entity) {
    return false;
  }

  @override
934
  Future<bool> isUpToDate() async {
935 936 937 938 939 940 941 942 943 944 945
    return true;
  }

  @override
  void setStampFor(String artifactName, String version) {
    throw UnsupportedError('Not supported in the fake Cache');
  }

  @override
  Future<void> updateAll(Set<DevelopmentArtifact> requiredArtifacts) async {
  }
946 947 948 949 950

  @override
  Future<bool> doesRemoteExist(String message, Uri url) async {
    return true;
  }
951 952 953

  @override
  void clearStampFiles() {}
954
}