// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. part of "dart:io"; final _ioOverridesToken = Object(); /// Facilities for overriding various APIs of `dart:io` with mock /// implementations. /// /// This abstract base class should be extended with overrides for the /// operations needed to construct mocks. The implementations in this base class /// default to the actual `dart:io` implementation. For example: /// /// ```dart /// class MyDirectory implements Directory { /// ... /// // An implementation of the Directory interface /// ... /// } /// /// void main() { /// IOOverrides.runZoned(() { /// ... /// // Operations will use MyDirectory instead of dart:io's Directory /// // implementation whenever Directory is used. /// ... /// }, createDirectory: (String path) => new MyDirectory(path)); /// } /// ``` abstract base class IOOverrides { static IOOverrides? _global; static IOOverrides? get current { return Zone.current[_ioOverridesToken] ?? _global; } /// The [IOOverrides] to use in the root [Zone]. /// /// These are the [IOOverrides] that will be used in the root [Zone], and in /// [Zone]'s that do not set [IOOverrides] and whose ancestors up to the root /// [Zone] also do not set [IOOverrides]. static set global(IOOverrides? overrides) { _global = overrides; } /// Runs [body] in a fresh [Zone] using the provided overrides. /// /// See the documentation on the corresponding methods of [IOOverrides] for /// information about what the optional arguments do. static R runZoned( R body(), { // Directory Directory Function(String)? createDirectory, Directory Function()? getCurrentDirectory, void Function(String)? setCurrentDirectory, Directory Function()? getSystemTempDirectory, // File File Function(String)? createFile, // FileStat Future Function(String)? stat, FileStat Function(String)? statSync, // FileSystemEntity Future Function(String, String)? fseIdentical, bool Function(String, String)? fseIdenticalSync, Future Function(String, bool)? fseGetType, FileSystemEntityType Function(String, bool)? fseGetTypeSync, // _FileSystemWatcher Stream Function(String, int, bool)? fsWatch, bool Function()? fsWatchIsSupported, // Link Link Function(String)? createLink, // Socket Future Function( dynamic, int, { dynamic sourceAddress, int sourcePort, Duration? timeout, })? socketConnect, Future> Function( dynamic, int, { dynamic sourceAddress, int sourcePort, })? socketStartConnect, // ServerSocket Future Function( dynamic, int, { int backlog, bool v6Only, bool shared, })? serverSocketBind, // Standard Streams Stdin Function()? stdin, Stdout Function()? stdout, Stdout Function()? stderr, // Process exit @Since("3.10") Never Function(int)? exit, }) { // Avoid building chains of override scopes. Just copy outer scope's // functions and `_previous`. var current = IOOverrides.current; _IOOverridesScope? currentScope; if (current is _IOOverridesScope) { currentScope = current; current = currentScope._previous; } IOOverrides overrides = _IOOverridesScope( current, // Directory createDirectory ?? currentScope?._createDirectory, getCurrentDirectory ?? currentScope?._getCurrentDirectory, setCurrentDirectory ?? currentScope?._setCurrentDirectory, getSystemTempDirectory ?? currentScope?._getSystemTempDirectory, // File createFile ?? currentScope?._createFile, // FileStat stat ?? currentScope?._stat, statSync ?? currentScope?._statSync, // FileSystemEntity fseIdentical ?? currentScope?._fseIdentical, fseIdenticalSync ?? currentScope?._fseIdenticalSync, fseGetType ?? currentScope?._fseGetType, fseGetTypeSync ?? currentScope?._fseGetTypeSync, // _FileSystemWatcher fsWatch ?? currentScope?._fsWatch, fsWatchIsSupported ?? currentScope?._fsWatchIsSupported, // Link createLink ?? currentScope?._createLink, // Socket socketConnect ?? currentScope?._socketConnect, socketStartConnect ?? currentScope?._socketStartConnect, // ServerSocket serverSocketBind ?? currentScope?._serverSocketBind, // Standard streams stdin ?? currentScope?._stdin, stdout ?? currentScope?._stdout, stderr ?? currentScope?._stderr, // Process exit exit ?? currentScope?._exit, ); return dart_async.runZoned( body, zoneValues: {_ioOverridesToken: overrides}, ); } /// Runs [body] in a fresh [Zone] using the overrides found in [overrides]. /// /// Note that [overrides] should be an instance of a class that extends /// [IOOverrides]. static R runWithIOOverrides(R body(), IOOverrides overrides) { return dart_async.runZoned( body, zoneValues: {_ioOverridesToken: overrides}, ); } // Directory /// Creates a new [Directory] object for the given [path]. /// /// When this override is installed, this function overrides the behavior of /// `new Directory()` and `new Directory.fromUri()`. Directory createDirectory(String path) => _Directory(path); /// Returns the current working directory. /// /// When this override is installed, this function overrides the behavior of /// the static getter `Directory.current` Directory getCurrentDirectory() => _Directory.current; /// Sets the current working directory to be [path]. /// /// When this override is installed, this function overrides the behavior of /// the setter `Directory.current`. void setCurrentDirectory(String path) { _Directory.current = path; } /// Returns the system temporary directory. /// /// When this override is installed, this function overrides the behavior of /// `Directory.systemTemp`. Directory getSystemTempDirectory() => _Directory.systemTemp; // File /// Creates a new [File] object for the given [path]. /// /// When this override is installed, this function overrides the behavior of /// `new File()` and `new File.fromUri()`. File createFile(String path) => _File(path); // FileStat /// Asynchronously returns [FileStat] information for [path]. /// /// When this override is installed, this function overrides the behavior of /// `FileStat.stat()`. Future stat(String path) { return FileStat._stat(path); } /// Returns [FileStat] information for [path]. /// /// When this override is installed, this function overrides the behavior of /// `FileStat.statSync()`. FileStat statSync(String path) { return FileStat._statSyncInternal(path); } // FileSystemEntity /// Asynchronously returns `true` if [path1] and [path2] are paths to the /// same file system object. /// /// When this override is installed, this function overrides the behavior of /// `FileSystemEntity.identical`. Future fseIdentical(String path1, String path2) { return FileSystemEntity._identical(path1, path2); } /// Returns `true` if [path1] and [path2] are paths to the /// same file system object. /// /// When this override is installed, this function overrides the behavior of /// `FileSystemEntity.identicalSync`. bool fseIdenticalSync(String path1, String path2) { return FileSystemEntity._identicalSync(path1, path2); } /// Asynchronously returns the [FileSystemEntityType] for [path]. /// /// When this override is installed, this function overrides the behavior of /// `FileSystemEntity.type`. Future fseGetType(String path, bool followLinks) { return FileSystemEntity._getTypeRequest(utf8.encode(path), followLinks); } /// Returns the [FileSystemEntityType] for [path]. /// /// When this override is installed, this function overrides the behavior of /// `FileSystemEntity.typeSync`. FileSystemEntityType fseGetTypeSync(String path, bool followLinks) { return FileSystemEntity._getTypeSyncHelper(utf8.encode(path), followLinks); } // _FileSystemWatcher /// Returns a [Stream] of [FileSystemEvent]s. /// /// When this override is installed, this function overrides the behavior of /// `FileSystemEntity.watch()`. Stream fsWatch(String path, int events, bool recursive) { return _FileSystemWatcher._watch(path, events, recursive); } /// Returns `true` when [FileSystemEntity.watch] is supported. /// /// When this override is installed, this function overrides the behavior of /// `FileSystemEntity.isWatchSupported`. bool fsWatchIsSupported() => _FileSystemWatcher.isSupported; // Link /// Returns a new [Link] object for the given [path]. /// /// When this override is installed, this function overrides the behavior of /// `new Link()` and `new Link.fromUri()`. Link createLink(String path) => _Link(path); // Socket /// Asynchronously returns a [Socket] connected to the given host and port. /// /// When this override is installed, this function overrides the behavior of /// `Socket.connect(...)`. Future socketConnect( host, int port, { sourceAddress, int sourcePort = 0, Duration? timeout, }) { return Socket._connect( host, port, sourceAddress: sourceAddress, sourcePort: sourcePort, timeout: timeout, ); } /// Asynchronously returns a [ConnectionTask] that connects to the given host /// and port when successful. /// /// When this override is installed, this function overrides the behavior of /// `Socket.startConnect(...)`. Future> socketStartConnect( host, int port, { sourceAddress, int sourcePort = 0, }) { return Socket._startConnect( host, port, sourceAddress: sourceAddress, sourcePort: sourcePort, ); } // ServerSocket /// Asynchronously returns a [ServerSocket] that connects to the given address /// and port when successful. /// /// When this override is installed, this function overrides the behavior of /// `ServerSocket.bind(...)`. Future serverSocketBind( address, int port, { int backlog = 0, bool v6Only = false, bool shared = false, }) { return ServerSocket._bind( address, port, backlog: backlog, v6Only: v6Only, shared: shared, ); } // Process exit /// Exit the Dart VM process immediately with the given exit code. /// /// When this override is installed, this function overrides the behavior of /// `exit(...)`. @Since("3.10") Never exit(int code) { if (!_EmbedderConfig._mayExit) { throw new UnsupportedError( "This embedder disallows calling dart:io's exit()", ); } _ProcessUtils._exit(code); } // Standard streams /// The standard input stream of data read by this program. /// /// When this override is installed, this getter overrides the behavior of /// the top-level `stdin` getter. Stdin get stdin { return _stdin; } /// The standard output stream of data written by this program. /// /// When this override is installed, this getter overrides the behavior of /// the top-level `stdout` getter. Stdout get stdout { return _stdout; } /// The standard output stream of errors written by this program. /// /// When this override is installed, this getter overrides the behavior of /// the top-level `stderr` getter. Stdout get stderr { return _stderr; } } final class _IOOverridesScope extends IOOverrides { final IOOverrides? _previous; // Directory final Directory Function(String)? _createDirectory; final Directory Function()? _getCurrentDirectory; final void Function(String)? _setCurrentDirectory; final Directory Function()? _getSystemTempDirectory; // File final File Function(String)? _createFile; // FileStat final Future Function(String)? _stat; final FileStat Function(String)? _statSync; // FileSystemEntity final Future Function(String, String)? _fseIdentical; final bool Function(String, String)? _fseIdenticalSync; final Future Function(String, bool)? _fseGetType; final FileSystemEntityType Function(String, bool)? _fseGetTypeSync; // _FileSystemWatcher final Stream Function(String, int, bool)? _fsWatch; final bool Function()? _fsWatchIsSupported; // Link final Link Function(String)? _createLink; // Socket final Future Function( dynamic, int, { dynamic sourceAddress, int sourcePort, Duration? timeout, })? _socketConnect; final Future> Function( dynamic, int, { dynamic sourceAddress, int sourcePort, })? _socketStartConnect; // ServerSocket final Future Function( dynamic, int, { int backlog, bool v6Only, bool shared, })? _serverSocketBind; // Standard streams final Stdin Function()? _stdin; final Stdout Function()? _stdout; final Stdout Function()? _stderr; // Process exit final Never Function(int)? _exit; _IOOverridesScope( this._previous, // Directory this._createDirectory, this._getCurrentDirectory, this._setCurrentDirectory, this._getSystemTempDirectory, // File this._createFile, // FileStat this._stat, this._statSync, // FileSystemEntity this._fseIdentical, this._fseIdenticalSync, this._fseGetType, this._fseGetTypeSync, // _FileSystemWatcher this._fsWatch, this._fsWatchIsSupported, // Link this._createLink, // Socket this._socketConnect, this._socketStartConnect, // ServerSocket this._serverSocketBind, // Standard streams this._stdin, this._stdout, this._stderr, // Process exit this._exit, ); // Directory @override Directory createDirectory(String path) => _createDirectory?.call(path) ?? _previous?.createDirectory(path) ?? super.createDirectory(path); @override Directory getCurrentDirectory() => _getCurrentDirectory?.call() ?? _previous?.getCurrentDirectory() ?? super.getCurrentDirectory(); @override void setCurrentDirectory(String path) { var setter = _setCurrentDirectory; if (setter != null) { setter(path); } else { super.setCurrentDirectory(path); } } @override Directory getSystemTempDirectory() => _getSystemTempDirectory?.call() ?? _previous?.getSystemTempDirectory() ?? super.getSystemTempDirectory(); // File @override File createFile(String path) => _createFile?.call(path) ?? _previous?.createFile(path) ?? super.createFile(path); // FileStat @override Future stat(String path) => _stat?.call(path) ?? _previous?.stat(path) ?? super.stat(path); @override FileStat statSync(String path) => _statSync?.call(path) ?? _previous?.statSync(path) ?? super.statSync(path); // FileSystemEntity @override Future fseIdentical(String path1, String path2) => _fseIdentical?.call(path1, path2) ?? _previous?.fseIdentical(path1, path2) ?? super.fseIdentical(path1, path2); @override bool fseIdenticalSync(String path1, String path2) => _fseIdenticalSync?.call(path1, path2) ?? _previous?.fseIdenticalSync(path1, path2) ?? super.fseIdenticalSync(path1, path2); @override Future fseGetType(String path, bool followLinks) => _fseGetType?.call(path, followLinks) ?? _previous?.fseGetType(path, followLinks) ?? super.fseGetType(path, followLinks); @override FileSystemEntityType fseGetTypeSync(String path, bool followLinks) => _fseGetTypeSync?.call(path, followLinks) ?? _previous?.fseGetTypeSync(path, followLinks) ?? super.fseGetTypeSync(path, followLinks); // _FileSystemWatcher @override Stream fsWatch(String path, int events, bool recursive) => _fsWatch?.call(path, events, recursive) ?? _previous?.fsWatch(path, events, recursive) ?? super.fsWatch(path, events, recursive); @override bool fsWatchIsSupported() => _fsWatchIsSupported?.call() ?? _previous?.fsWatchIsSupported() ?? super.fsWatchIsSupported(); // Link @override Link createLink(String path) => _createLink?.call(path) ?? _previous?.createLink(path) ?? super.createLink(path); // Socket @override Future socketConnect( host, int port, { sourceAddress, int sourcePort = 0, Duration? timeout, }) => _socketConnect?.call( host, port, sourceAddress: sourceAddress, sourcePort: sourcePort, timeout: timeout, ) ?? _previous?.socketConnect( host, port, sourceAddress: sourceAddress, sourcePort: sourcePort, timeout: timeout, ) ?? super.socketConnect( host, port, sourceAddress: sourceAddress, sourcePort: sourcePort, timeout: timeout, ); @override Future> socketStartConnect( host, int port, { sourceAddress, int sourcePort = 0, }) => _socketStartConnect?.call( host, port, sourceAddress: sourceAddress, sourcePort: sourcePort, ) ?? _previous?.socketStartConnect( host, port, sourceAddress: sourceAddress, sourcePort: sourcePort, ) ?? super.socketStartConnect( host, port, sourceAddress: sourceAddress, sourcePort: sourcePort, ); // ServerSocket @override Future serverSocketBind( address, int port, { int backlog = 0, bool v6Only = false, bool shared = false, }) => _serverSocketBind?.call( address, port, backlog: backlog, v6Only: v6Only, shared: shared, ) ?? _previous?.serverSocketBind( address, port, backlog: backlog, v6Only: v6Only, shared: shared, ) ?? super.serverSocketBind( address, port, backlog: backlog, v6Only: v6Only, shared: shared, ); // Process exit @override Never exit(int code) => _exit?.call(code) ?? _previous?.exit.call(code) ?? super.exit(code); // Standard streams @override Stdin get stdin => _stdin?.call() ?? _previous?.stdin ?? super.stdin; @override Stdout get stdout => _stdout?.call() ?? _previous?.stdout ?? super.stdout; @override Stdout get stderr => _stderr?.call() ?? _previous?.stderr ?? super.stderr; }