io.dart 5.85 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

/// This file serves as the single point of entry into the `dart:io` APIs
/// within Flutter tools.
///
/// In order to make Flutter tools more testable, we use the `FileSystem` APIs
/// in `package:file` rather than using the `dart:io` file APIs directly (see
/// `file_system.dart`). Doing so allows us to swap out local file system
/// access with mockable (or in-memory) file systems, making our tests hermetic
/// vis-a-vis file system access.
///
14 15 16
/// We also use `package:platform` to provide an abstraction away from the
/// static methods in the `dart:io` `Platform` class (see `platform.dart`). As
/// such, do not export Platform from this file!
17
///
18 19 20 21 22 23 24 25 26 27
/// To ensure that all file system and platform API access within Flutter tools
/// goes through the proper APIs, we forbid direct imports of `dart:io` (via a
/// test), forcing all callers to instead import this file, which exports the
/// blessed subset of `dart:io` that is legal to use in Flutter tools.
///
/// Because of the nature of this file, it is important that **platform and file
/// APIs not be exported from `dart:io` in this file**! Moreover, be careful
/// about any additional exports that you add to this file, as doing so will
/// increase the API surface that we have to test in Flutter tools, and the APIs
/// in `dart:io` can sometimes be hard to use in tests.
28
import 'dart:async';
29
import 'dart:io' as io show exit, IOSink, ProcessSignal, stderr, stdin, stdout;
30 31 32

import 'package:meta/meta.dart';

33
import 'context.dart';
34
import 'platform.dart';
35 36
import 'process.dart';

37 38 39
export 'dart:io'
    show
        BytesBuilder,
40
        // Directory         NO! Use `file_system.dart`
41
        exitCode,
42 43
        // File              NO! Use `file_system.dart`
        // FileSystemEntity  NO! Use `file_system.dart`
44 45 46 47 48 49 50 51
        GZIP,
        HttpClient,
        HttpClientRequest,
        HttpClientResponse,
        HttpHeaders,
        HttpRequest,
        HttpServer,
        HttpStatus,
52 53 54 55
        InternetAddress,
        InternetAddressType,
        IOException,
        IOSink,
56
        // Link              NO! Use `file_system.dart`
57
        pid,
58
        // Platform          NO! use `platform.dart`
59 60 61
        Process,
        ProcessException,
        ProcessResult,
62
        // ProcessSignal     NO! Use [ProcessSignal] below.
63
        ProcessStartMode,
64
        // RandomAccessFile  NO! Use `file_system.dart`
65
        ServerSocket,
66 67 68
        // stderr,           NO! Use `io.dart`
        // stdin,            NO! Use `io.dart`
        Stdin,
69
        StdinException,
70
        // stdout,           NO! Use `io.dart`
71 72 73 74 75 76 77 78 79
        Socket,
        SocketException,
        SYSTEM_ENCODING,
        WebSocket,
        WebSocketTransformer;

/// Exits the process with the given [exitCode].
typedef void ExitFunction(int exitCode);

80
final ExitFunction _defaultExitFunction = io.exit;
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97

ExitFunction _exitFunction = _defaultExitFunction;

/// Exits the process.
///
/// This is analogous to the `exit` function in `dart:io`, except that this
/// function may be set to a testing-friendly value by calling
/// [setExitFunctionForTests] (and then restored to its default implementation
/// with [restoreExitFunction]). The default implementation delegates to
/// `dart:io`.
ExitFunction get exit => _exitFunction;

/// Sets the [exit] function to a function that throws an exception rather
/// than exiting the process; this is intended for testing purposes.
@visibleForTesting
void setExitFunctionForTests([ExitFunction exitFunction]) {
  _exitFunction = exitFunction ?? (int exitCode) {
98
    throw new ProcessExit(exitCode, immediate: true);
99 100 101 102 103 104 105 106
  };
}

/// Restores the [exit] function to the `dart:io` implementation.
@visibleForTesting
void restoreExitFunction() {
  _exitFunction = _defaultExitFunction;
}
107 108 109 110 111 112 113 114 115 116 117 118 119 120

/// A portable version of [io.ProcessSignal].
///
/// Listening on signals that don't exist on the current platform is just a
/// no-op. This is in contrast to [io.ProcessSignal], where listening to
/// non-existent signals throws an exception.
class ProcessSignal implements io.ProcessSignal {
  @visibleForTesting
  const ProcessSignal(this._delegate);

  static const ProcessSignal SIGWINCH = const _PosixProcessSignal._(io.ProcessSignal.SIGWINCH);
  static const ProcessSignal SIGTERM = const _PosixProcessSignal._(io.ProcessSignal.SIGTERM);
  static const ProcessSignal SIGUSR1 = const _PosixProcessSignal._(io.ProcessSignal.SIGUSR1);
  static const ProcessSignal SIGUSR2 = const _PosixProcessSignal._(io.ProcessSignal.SIGUSR2);
121
  static const ProcessSignal SIGINT =  const ProcessSignal(io.ProcessSignal.SIGINT);
122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143

  final io.ProcessSignal _delegate;

  @override
  Stream<ProcessSignal> watch() {
    return _delegate.watch().map((io.ProcessSignal signal) => this);
  }

  @override
  String toString() => _delegate.toString();
}

/// A [ProcessSignal] that is only available on Posix platforms.
///
/// Listening to a [_PosixProcessSignal] is a no-op on Windows.
class _PosixProcessSignal extends ProcessSignal {

  const _PosixProcessSignal._(io.ProcessSignal wrappedSignal) : super(wrappedSignal);

  @override
  Stream<ProcessSignal> watch() {
    if (platform.isWindows)
144
      return const Stream<ProcessSignal>.empty();
145 146 147
    return super.watch();
  }
}
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176

class Stdio {
  const Stdio();

  Stream<List<int>> get stdin => io.stdin;
  io.IOSink get stdout => io.stdout;
  io.IOSink get stderr => io.stderr;
}

io.IOSink get stderr {
  if (context == null)
    return io.stderr;
  final Stdio contextStreams = context[Stdio];
  return contextStreams.stderr;
}

Stream<List<int>> get stdin {
  if (context == null)
    return io.stdin;
  final Stdio contextStreams = context[Stdio];
  return contextStreams.stdin;
}

io.IOSink get stdout {
  if (context == null)
    return io.stdout;
  final Stdio contextStreams = context[Stdio];
  return contextStreams.stdout;
}