mirror of
https://github.com/dart-lang/sdk
synced 2024-10-03 00:09:20 +00:00
[dart:io] Begins work on mocking support
Change-Id: I770ed9485a934af07e570fbad3f3fb84ebef973d Reviewed-on: https://dart-review.googlesource.com/3302 Commit-Queue: Zach Anderson <zra@google.com> Reviewed-by: Ryan Macnak <rmacnak@google.com> Reviewed-by: Florian Loitsch <floitsch@google.com>
This commit is contained in:
parent
c5ff5b4011
commit
dcd275fa74
16
CHANGELOG.md
16
CHANGELOG.md
|
@ -24,16 +24,18 @@
|
|||
methods are now supported on iOS and OSX.
|
||||
* Deprecated `SecurityContext.alpnSupported` as ALPN is now supported on all
|
||||
platforms.
|
||||
* Added 'timeout' parameter to 'Socket.connect', 'RawSocket.connect',
|
||||
'SecureSocket.connect' and 'RawSecureSocket.connect. If a connection attempt
|
||||
takes longer than the duration specified in 'timeout', a 'SocketException'
|
||||
will be thrown. Note: if the duration specified in 'timeout' is greater than
|
||||
the system level timeout duration, a timeout may occur sooner than specified
|
||||
in 'timeout'.
|
||||
* Added a `timeout` parameter to `Socket.connect`, `RawSocket.connect`,
|
||||
`SecureSocket.connect` and `RawSecureSocket.connect`. If a connection attempt
|
||||
takes longer than the duration specified in `timeout`, a `SocketException`
|
||||
will be thrown. Note: if the duration specified in `timeout` is greater than
|
||||
the OS level timeout, a timeout may occur sooner than specified in
|
||||
`timeout`.
|
||||
* Added `Platform.operatingSystemVersion` that gives a platform-specific
|
||||
String describing the version of the operating system.
|
||||
* Added `RawZLibFilter` for low-level access to compression and
|
||||
decompression.
|
||||
decompression routines.
|
||||
* Added `IoOverrides` and `HttpOverrides` to aid in writing tests that wish to
|
||||
mock varios `dart:io` objects.
|
||||
|
||||
* `dart:core`
|
||||
* The `Uri` class now correctly handles paths while running on Node.js on
|
||||
|
|
|
@ -84,12 +84,12 @@ class FileStat {
|
|||
@patch
|
||||
class FileSystemEntity {
|
||||
@patch
|
||||
static _getType(_Namespace namespace, String path, bool followLinks) {
|
||||
static _getTypeNative(_Namespace namespace, String path, bool followLinks) {
|
||||
throw new UnsupportedError("FileSystemEntity._getType");
|
||||
}
|
||||
|
||||
@patch
|
||||
static _identical(_Namespace namespace, String path1, String path2) {
|
||||
static _identicalNative(_Namespace namespace, String path1, String path2) {
|
||||
throw new UnsupportedError("FileSystemEntity._identical");
|
||||
}
|
||||
|
||||
|
|
|
@ -13,10 +13,10 @@ class FileStat {
|
|||
@patch
|
||||
class FileSystemEntity {
|
||||
@patch
|
||||
static _getType(_Namespace namespace, String path, bool followLinks)
|
||||
static _getTypeNative(_Namespace namespace, String path, bool followLinks)
|
||||
native "File_GetType";
|
||||
@patch
|
||||
static _identical(_Namespace namespace, String path1, String path2)
|
||||
static _identicalNative(_Namespace namespace, String path1, String path2)
|
||||
native "File_AreIdentical";
|
||||
@patch
|
||||
static _resolveSymbolicLinks(_Namespace namespace, String path)
|
||||
|
|
|
@ -26,6 +26,7 @@ part 'http_headers.dart';
|
|||
part 'http_impl.dart';
|
||||
part 'http_parser.dart';
|
||||
part 'http_session.dart';
|
||||
part 'overrides.dart';
|
||||
part 'websocket.dart';
|
||||
part 'websocket_impl.dart';
|
||||
|
||||
|
@ -1363,7 +1364,13 @@ abstract class HttpClient {
|
|||
*/
|
||||
String userAgent;
|
||||
|
||||
factory HttpClient({SecurityContext context}) => new _HttpClient(context);
|
||||
factory HttpClient({SecurityContext context}) {
|
||||
HttpOverrides overrides = HttpOverrides.current;
|
||||
if (overrides == null) {
|
||||
return new _HttpClient(context);
|
||||
}
|
||||
return overrides.createHttpClient(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a HTTP connection.
|
||||
|
@ -1623,7 +1630,11 @@ abstract class HttpClient {
|
|||
*/
|
||||
static String findProxyFromEnvironment(Uri url,
|
||||
{Map<String, String> environment}) {
|
||||
return _HttpClient._findProxyFromEnvironment(url, environment);
|
||||
HttpOverrides overrides = HttpOverrides.current;
|
||||
if (overrides == null) {
|
||||
return _HttpClient._findProxyFromEnvironment(url, environment);
|
||||
}
|
||||
return overrides.findProxyFromEnvironment(url, environment);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -12,6 +12,7 @@ http_sdk_sources = [
|
|||
"http_impl.dart",
|
||||
"http_parser.dart",
|
||||
"http_session.dart",
|
||||
"overrides.dart",
|
||||
"websocket.dart",
|
||||
"websocket_impl.dart",
|
||||
]
|
||||
|
|
107
sdk/lib/_http/overrides.dart
Normal file
107
sdk/lib/_http/overrides.dart
Normal file
|
@ -0,0 +1,107 @@
|
|||
// 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._http;
|
||||
|
||||
final _httpOverridesToken = new Object();
|
||||
|
||||
const _asyncRunZoned = runZoned;
|
||||
|
||||
/// This class facilitates overriding [HttpClient] with a mock implementation.
|
||||
/// It should be extended by another class in client code with overrides
|
||||
/// that construct a mock implementation. The implementation in this base class
|
||||
/// defaults to the actual [HttpClient] implementation. For example:
|
||||
///
|
||||
/// ```
|
||||
/// class MyHttpClient implements HttpClient {
|
||||
/// ...
|
||||
/// // An implementation of the HttpClient interface
|
||||
/// ...
|
||||
/// }
|
||||
///
|
||||
/// main() {
|
||||
/// HttpOverrides.runZoned(() {
|
||||
/// ...
|
||||
/// // Operations will use MyHttpClient instead of the real HttpClient
|
||||
/// // implementation whenever HttpClient is used.
|
||||
/// ...
|
||||
/// }, createHttpClient: (SecurityContext c) => new MyHttpClient(c));
|
||||
/// }
|
||||
/// ```
|
||||
abstract class HttpOverrides {
|
||||
static HttpOverrides get current {
|
||||
return Zone.current[_httpOverridesToken];
|
||||
}
|
||||
|
||||
/// Runs [body] in a fresh [Zone] using the provided overrides.
|
||||
static R runZoned<R>(R body(),
|
||||
{HttpClient Function(SecurityContext) createHttpClient,
|
||||
String Function(Uri uri, Map<String, String> environment)
|
||||
findProxyFromEnvironment,
|
||||
ZoneSpecification zoneSpecification,
|
||||
Function onError}) {
|
||||
HttpOverrides overrides =
|
||||
new _HttpOverridesScope(createHttpClient, findProxyFromEnvironment);
|
||||
return _asyncRunZoned<R>(body,
|
||||
zoneValues: {_httpOverridesToken: overrides},
|
||||
zoneSpecification: zoneSpecification,
|
||||
onError: onError);
|
||||
}
|
||||
|
||||
/// Runs [body] in a fresh [Zone] using the overrides found in [overrides].
|
||||
///
|
||||
/// Note that [overrides] should be an instance of a class that extends
|
||||
/// [HttpOverrides].
|
||||
static R runWithHttpOverrides<R>(R body(), HttpOverrides overrides,
|
||||
{ZoneSpecification zoneSpecification, Function onError}) {
|
||||
return _asyncRunZoned<R>(body,
|
||||
zoneValues: {_httpOverridesToken: overrides},
|
||||
zoneSpecification: zoneSpecification,
|
||||
onError: onError);
|
||||
}
|
||||
|
||||
/// Returns a new [HttpClient] using the given [context].
|
||||
///
|
||||
/// When this override is installed, this function overrides the behavior of
|
||||
/// `new HttpClient`.
|
||||
HttpClient createHttpClient(SecurityContext context) {
|
||||
return new _HttpClient(context);
|
||||
}
|
||||
|
||||
/// Resolves the proxy server to be used for HTTP connections.
|
||||
///
|
||||
/// When this override is installed, this function overrides the behavior of
|
||||
/// `HttpClient.findProxyFromEnvironment`.
|
||||
String findProxyFromEnvironment(Uri url, Map<String, String> environment) {
|
||||
return _HttpClient._findProxyFromEnvironment(url, environment);
|
||||
}
|
||||
}
|
||||
|
||||
class _HttpOverridesScope extends HttpOverrides {
|
||||
final HttpOverrides _previous = HttpOverrides.current;
|
||||
|
||||
final HttpClient Function(SecurityContext) _createHttpClient;
|
||||
final String Function(Uri uri, Map<String, String> environment)
|
||||
_findProxyFromEnvironment;
|
||||
|
||||
_HttpOverridesScope(this._createHttpClient, this._findProxyFromEnvironment);
|
||||
|
||||
@override
|
||||
HttpClient createHttpClient(SecurityContext context) {
|
||||
if (_createHttpClient != null) return _createHttpClient(context);
|
||||
if (_previous != null) return _previous.createHttpClient(context);
|
||||
return super.createHttpClient(context);
|
||||
}
|
||||
|
||||
@override
|
||||
String findProxyFromEnvironment(Uri url, Map<String, String> environment) {
|
||||
if (_findProxyFromEnvironment != null) {
|
||||
return _findProxyFromEnvironment(url, environment);
|
||||
}
|
||||
if (_previous != null) {
|
||||
return _previous.findProxyFromEnvironment(url, environment);
|
||||
}
|
||||
return super.findProxyFromEnvironment(url, environment);
|
||||
}
|
||||
}
|
|
@ -84,12 +84,12 @@ class FileStat {
|
|||
@patch
|
||||
class FileSystemEntity {
|
||||
@patch
|
||||
static _getType(_Namespace namespace, String path, bool followLinks) {
|
||||
static _getTypeNative(_Namespace namespace, String path, bool followLinks) {
|
||||
throw new UnsupportedError("FileSystemEntity._getType");
|
||||
}
|
||||
|
||||
@patch
|
||||
static _identical(_Namespace namespace, String path1, String path2) {
|
||||
static _identicalNative(_Namespace namespace, String path1, String path2) {
|
||||
throw new UnsupportedError("FileSystemEntity._identical");
|
||||
}
|
||||
|
||||
|
|
|
@ -126,7 +126,13 @@ abstract class Directory implements FileSystemEntity {
|
|||
* If [path] is an absolute path, it will be immune to changes to the
|
||||
* current working directory.
|
||||
*/
|
||||
factory Directory(String path) => new _Directory(path);
|
||||
factory Directory(String path) {
|
||||
final IoOverrides overrides = IoOverrides.current;
|
||||
if (overrides == null) {
|
||||
return new _Directory(path);
|
||||
}
|
||||
return overrides.createDirectory(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a Directory object from a URI.
|
||||
|
@ -139,7 +145,13 @@ abstract class Directory implements FileSystemEntity {
|
|||
* Creates a directory object pointing to the current working
|
||||
* directory.
|
||||
*/
|
||||
static Directory get current => _Directory.current;
|
||||
static Directory get current {
|
||||
final IoOverrides overrides = IoOverrides.current;
|
||||
if (overrides == null) {
|
||||
return _Directory.current;
|
||||
}
|
||||
return overrides.getCurrentDirectory();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a [Uri] representing the directory's location.
|
||||
|
@ -169,7 +181,12 @@ abstract class Directory implements FileSystemEntity {
|
|||
* are working with the file system, can lead to unexpected results.
|
||||
*/
|
||||
static void set current(path) {
|
||||
_Directory.current = path;
|
||||
final IoOverrides overrides = IoOverrides.current;
|
||||
if (overrides == null) {
|
||||
_Directory.current = path;
|
||||
return;
|
||||
}
|
||||
overrides.setCurrentDirectory(path);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -204,7 +221,13 @@ abstract class Directory implements FileSystemEntity {
|
|||
* The location of the system temp directory is platform-dependent,
|
||||
* and may be set by an environment variable.
|
||||
*/
|
||||
static Directory get systemTemp => _Directory.systemTemp;
|
||||
static Directory get systemTemp {
|
||||
final IoOverrides overrides = IoOverrides.current;
|
||||
if (overrides == null) {
|
||||
return _Directory.systemTemp;
|
||||
}
|
||||
return overrides.getSystemTempDirectory();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a temporary directory in this directory. Additional random
|
||||
|
|
|
@ -219,7 +219,13 @@ abstract class File implements FileSystemEntity {
|
|||
* If [path] is an absolute path, it will be immune to changes to the
|
||||
* current working directory.
|
||||
*/
|
||||
factory File(String path) => new _File(path);
|
||||
factory File(String path) {
|
||||
final IoOverrides overrides = IoOverrides.current;
|
||||
if (overrides == null) {
|
||||
return new _File(path);
|
||||
}
|
||||
return overrides.createFile(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a File object from a URI.
|
||||
|
|
|
@ -49,30 +49,40 @@ class FileStat {
|
|||
|
||||
/**
|
||||
* The time of the last change to the data or metadata of the file system
|
||||
* object. On Windows platforms, this is instead the file creation time.
|
||||
* object.
|
||||
*
|
||||
* On Windows platforms, this is instead the file creation time.
|
||||
*/
|
||||
final DateTime changed;
|
||||
|
||||
/**
|
||||
* The time of the last change to the data of the file system
|
||||
* object.
|
||||
* The time of the last change to the data of the file system object.
|
||||
*/
|
||||
final DateTime modified;
|
||||
|
||||
/**
|
||||
* The time of the last access to the data of the file system
|
||||
* object. On Windows platforms, this may have 1 day granularity, and be
|
||||
* The time of the last access to the data of the file system object.
|
||||
*
|
||||
* On Windows platforms, this may have 1 day granularity, and be
|
||||
* out of date by an hour.
|
||||
*/
|
||||
final DateTime accessed;
|
||||
|
||||
/**
|
||||
* The type of the object (file, directory, or link). If the call to
|
||||
* stat() fails, the type of the returned object is NOT_FOUND.
|
||||
* The type of the object (file, directory, or link).
|
||||
*
|
||||
* If the call to stat() fails, the type of the returned object is NOT_FOUND.
|
||||
*/
|
||||
final FileSystemEntityType type;
|
||||
|
||||
/**
|
||||
* The mode of the file system object. Permissions are encoded in the lower
|
||||
* 16 bits of this number, and can be decoded using the [modeString] getter.
|
||||
* The mode of the file system object.
|
||||
*
|
||||
* Permissions are encoded in the lower 16 bits of this number, and can be
|
||||
* decoded using the [modeString] getter.
|
||||
*/
|
||||
final int mode;
|
||||
|
||||
/**
|
||||
* The size of the file system object.
|
||||
*/
|
||||
|
@ -93,11 +103,20 @@ class FileStat {
|
|||
|
||||
/**
|
||||
* Calls the operating system's stat() function on [path].
|
||||
*
|
||||
* Returns a [FileStat] object containing the data returned by stat().
|
||||
* If the call fails, returns a [FileStat] object with .type set to
|
||||
* FileSystemEntityType.NOT_FOUND and the other fields invalid.
|
||||
*/
|
||||
static FileStat statSync(String path) {
|
||||
final IoOverrides overrides = IoOverrides.current;
|
||||
if (overrides == null) {
|
||||
return _statSyncInternal(path);
|
||||
}
|
||||
return overrides.statSync(path);
|
||||
}
|
||||
|
||||
static FileStat _statSyncInternal(String path) {
|
||||
// Trailing path is not supported on Windows.
|
||||
if (Platform.isWindows) {
|
||||
path = FileSystemEntity._trimTrailingPathSeparators(path);
|
||||
|
@ -115,12 +134,21 @@ class FileStat {
|
|||
|
||||
/**
|
||||
* Asynchronously calls the operating system's stat() function on [path].
|
||||
*
|
||||
* Returns a Future which completes with a [FileStat] object containing
|
||||
* the data returned by stat().
|
||||
* If the call fails, completes the future with a [FileStat] object with
|
||||
* .type set to FileSystemEntityType.NOT_FOUND and the other fields invalid.
|
||||
* the data returned by stat(). If the call fails, completes the future with a
|
||||
* [FileStat] object with `.type` set to FileSystemEntityType.NOT_FOUND and
|
||||
* the other fields invalid.
|
||||
*/
|
||||
static Future<FileStat> stat(String path) {
|
||||
final IoOverrides overrides = IoOverrides.current;
|
||||
if (overrides == null) {
|
||||
return _stat(path);
|
||||
}
|
||||
return overrides.stat(path);
|
||||
}
|
||||
|
||||
static Future<FileStat> _stat(String path) {
|
||||
// Trailing path is not supported on Windows.
|
||||
if (Platform.isWindows) {
|
||||
path = FileSystemEntity._trimTrailingPathSeparators(path);
|
||||
|
@ -151,11 +179,13 @@ FileStat: type $type
|
|||
size $size""";
|
||||
|
||||
/**
|
||||
* Returns the mode value as a human-readable string, in the format
|
||||
* "rwxrwxrwx", reflecting the user, group, and world permissions to
|
||||
* read, write, and execute the file system object, with "-" replacing the
|
||||
* letter for missing permissions. Extra permission bits may be represented
|
||||
* by prepending "(suid)", "(guid)", and/or "(sticky)" to the mode string.
|
||||
* Returns the mode value as a human-readable string.
|
||||
*
|
||||
* The string is in the format "rwxrwxrwx", reflecting the user, group, and
|
||||
* world permissions to read, write, and execute the file system object, with
|
||||
* "-" replacing the letter for missing permissions. Extra permission bits
|
||||
* may be represented by prepending "(suid)", "(guid)", and/or "(sticky)" to
|
||||
* the mode string.
|
||||
*/
|
||||
String modeString() {
|
||||
var permissions = mode & 0xFFF;
|
||||
|
@ -255,9 +285,10 @@ abstract class FileSystemEntity {
|
|||
bool existsSync();
|
||||
|
||||
/**
|
||||
* Renames this file system entity. Returns a `Future<FileSystemEntity>`
|
||||
* that completes with a [FileSystemEntity] instance for the renamed
|
||||
* file system entity.
|
||||
* Renames this file system entity.
|
||||
*
|
||||
* Returns a `Future<FileSystemEntity>` that completes with a
|
||||
* [FileSystemEntity] instance for the renamed file system entity.
|
||||
*
|
||||
* If [newPath] identifies an existing entity of the same type, that entity
|
||||
* is replaced. If [newPath] identifies an existing entity of a different
|
||||
|
@ -266,8 +297,9 @@ abstract class FileSystemEntity {
|
|||
Future<FileSystemEntity> rename(String newPath);
|
||||
|
||||
/**
|
||||
* Synchronously renames this file system entity. Returns a [FileSystemEntity]
|
||||
* instance for the renamed entity.
|
||||
* Synchronously renames this file system entity.
|
||||
*
|
||||
* Returns a [FileSystemEntity] instance for the renamed entity.
|
||||
*
|
||||
* If [newPath] identifies an existing entity of the same type, that entity
|
||||
* is replaced. If [newPath] identifies an existing entity of a different
|
||||
|
@ -277,8 +309,10 @@ abstract class FileSystemEntity {
|
|||
|
||||
/**
|
||||
* Resolves the path of a file system object relative to the
|
||||
* current working directory, resolving all symbolic links on
|
||||
* the path and resolving all `..` and `.` path segments.
|
||||
* current working directory.
|
||||
*
|
||||
* Resolves all symbolic links on the path and resolves all `..` and `.` path
|
||||
* segments.
|
||||
*
|
||||
* [resolveSymbolicLinks] uses the operating system's native
|
||||
* file system API to resolve the path, using the `realpath` function
|
||||
|
@ -316,8 +350,10 @@ abstract class FileSystemEntity {
|
|||
|
||||
/**
|
||||
* Resolves the path of a file system object relative to the
|
||||
* current working directory, resolving all symbolic links on
|
||||
* the path and resolving all `..` and `.` path segments.
|
||||
* current working directory.
|
||||
*
|
||||
* Resolves all symbolic links on the path and resolves all `..` and `.` path
|
||||
* segments.
|
||||
*
|
||||
* [resolveSymbolicLinksSync] uses the operating system's native
|
||||
* file system API to resolve the path, using the `realpath` function
|
||||
|
@ -349,7 +385,9 @@ abstract class FileSystemEntity {
|
|||
|
||||
/**
|
||||
* Calls the operating system's stat() function on the [path] of this
|
||||
* [FileSystemEntity]. Identical to [:FileStat.stat(this.path):].
|
||||
* [FileSystemEntity].
|
||||
*
|
||||
* Identical to [:FileStat.stat(this.path):].
|
||||
*
|
||||
* Returns a [:Future<FileStat>:] object containing the data returned by
|
||||
* stat().
|
||||
|
@ -363,6 +401,7 @@ abstract class FileSystemEntity {
|
|||
/**
|
||||
* Synchronously calls the operating system's stat() function on the
|
||||
* [path] of this [FileSystemEntity].
|
||||
*
|
||||
* Identical to [:FileStat.statSync(this.path):].
|
||||
*
|
||||
* Returns a [FileStat] object containing the data returned by stat().
|
||||
|
@ -443,16 +482,34 @@ abstract class FileSystemEntity {
|
|||
* A move event may be reported as seperate delete and create events.
|
||||
*/
|
||||
Stream<FileSystemEvent> watch(
|
||||
{int events: FileSystemEvent.ALL, bool recursive: false}) =>
|
||||
_FileSystemWatcher._watch(
|
||||
_trimTrailingPathSeparators(path), events, recursive);
|
||||
{int events: FileSystemEvent.ALL, bool recursive: false}) {
|
||||
final String trimmedPath = _trimTrailingPathSeparators(path);
|
||||
final IoOverrides overrides = IoOverrides.current;
|
||||
if (overrides == null) {
|
||||
return _FileSystemWatcher._watch(trimmedPath, events, recursive);
|
||||
}
|
||||
return overrides.fsWatch(trimmedPath, events, recursive);
|
||||
}
|
||||
|
||||
Future<FileSystemEntity> _delete({bool recursive: false});
|
||||
void _deleteSync({bool recursive: false});
|
||||
|
||||
static Future<bool> _identical(String path1, String path2) {
|
||||
return _File._dispatchWithNamespace(
|
||||
_FILE_IDENTICAL, [null, path1, path2]).then((response) {
|
||||
if (_isErrorResponse(response)) {
|
||||
throw _exceptionFromResponse(response,
|
||||
"Error in FileSystemEntity.identical($path1, $path2)", "");
|
||||
}
|
||||
return response;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether two paths refer to the same object in the
|
||||
* file system. Returns a [:Future<bool>:] that completes with the result.
|
||||
* file system.
|
||||
*
|
||||
* Returns a [:Future<bool>:] that completes with the result.
|
||||
*
|
||||
* Comparing a link to its target returns false, as does comparing two links
|
||||
* that point to the same target. To check the target of a link, use
|
||||
|
@ -463,14 +520,11 @@ abstract class FileSystemEntity {
|
|||
* to an object that does not exist.
|
||||
*/
|
||||
static Future<bool> identical(String path1, String path2) {
|
||||
return _File._dispatchWithNamespace(
|
||||
_FILE_IDENTICAL, [null, path1, path2]).then((response) {
|
||||
if (_isErrorResponse(response)) {
|
||||
throw _exceptionFromResponse(response,
|
||||
"Error in FileSystemEntity.identical($path1, $path2)", "");
|
||||
}
|
||||
return response;
|
||||
});
|
||||
IoOverrides overrides = IoOverrides.current;
|
||||
if (overrides == null) {
|
||||
return _identical(path1, path2);
|
||||
}
|
||||
return overrides.fseIdentical(path1, path2);
|
||||
}
|
||||
|
||||
static final RegExp _absoluteWindowsPathPattern =
|
||||
|
@ -493,6 +547,7 @@ abstract class FileSystemEntity {
|
|||
|
||||
/**
|
||||
* Returns a [FileSystemEntity] whose path is the absolute path to [this].
|
||||
*
|
||||
* The type of the returned instance is the type of [this].
|
||||
*
|
||||
* The absolute path is computed by prefixing
|
||||
|
@ -512,6 +567,12 @@ abstract class FileSystemEntity {
|
|||
}
|
||||
}
|
||||
|
||||
static bool _identicalSync(String path1, String path2) {
|
||||
var result = _identicalNative(_Namespace._namespace, path1, path2);
|
||||
_throwIfError(result, 'Error in FileSystemEntity.identicalSync');
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronously checks whether two paths refer to the same object in the
|
||||
* file system.
|
||||
|
@ -525,9 +586,11 @@ abstract class FileSystemEntity {
|
|||
* exist.
|
||||
*/
|
||||
static bool identicalSync(String path1, String path2) {
|
||||
var result = _identical(_Namespace._namespace, path1, path2);
|
||||
_throwIfError(result, 'Error in FileSystemEntity.identicalSync');
|
||||
return result;
|
||||
IoOverrides overrides = IoOverrides.current;
|
||||
if (overrides == null) {
|
||||
return _identicalSync(path1, path2);
|
||||
}
|
||||
return overrides.fseIdenticalSync(path1, path2);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -535,11 +598,18 @@ abstract class FileSystemEntity {
|
|||
*
|
||||
* OS X 10.6 and below is not supported.
|
||||
*/
|
||||
static bool get isWatchSupported => _FileSystemWatcher.isSupported;
|
||||
static bool get isWatchSupported {
|
||||
final IoOverrides overrides = IoOverrides.current;
|
||||
if (overrides == null) {
|
||||
return _FileSystemWatcher.isSupported;
|
||||
}
|
||||
return overrides.fsWatchIsSupported();
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the type of file system object that a path points to. Returns
|
||||
* a [:Future<FileSystemEntityType>:] that completes with the result.
|
||||
* Finds the type of file system object that a path points to.
|
||||
*
|
||||
* Returns a [:Future<FileSystemEntityType>:] that completes with the result.
|
||||
*
|
||||
* [FileSystemEntityType] has the constant instances FILE, DIRECTORY,
|
||||
* LINK, and NOT_FOUND. [type] will return LINK only if the optional
|
||||
|
@ -550,11 +620,13 @@ abstract class FileSystemEntity {
|
|||
* caused by passing the wrong type of arguments to the function.
|
||||
*/
|
||||
static Future<FileSystemEntityType> type(String path,
|
||||
{bool followLinks: true}) =>
|
||||
_getTypeAsync(path, followLinks).then(FileSystemEntityType._lookup);
|
||||
{bool followLinks: true}) {
|
||||
return _getType(path, followLinks);
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronously finds the type of file system object that a path points to.
|
||||
*
|
||||
* Returns a [FileSystemEntityType].
|
||||
*
|
||||
* [FileSystemEntityType] has the constant instances FILE, DIRECTORY,
|
||||
|
@ -565,51 +637,53 @@ abstract class FileSystemEntity {
|
|||
* error or exception that may be thrown is ArgumentError,
|
||||
* caused by passing the wrong type of arguments to the function.
|
||||
*/
|
||||
static FileSystemEntityType typeSync(String path, {bool followLinks: true}) =>
|
||||
FileSystemEntityType._lookup(_getTypeSync(path, followLinks));
|
||||
static FileSystemEntityType typeSync(String path, {bool followLinks: true}) {
|
||||
return _getTypeSync(path, followLinks);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if type(path, followLinks: false) returns
|
||||
* FileSystemEntityType.LINK.
|
||||
* Checks if type(path, followLinks: false) returns FileSystemEntityType.LINK.
|
||||
*/
|
||||
static Future<bool> isLink(String path) => _getTypeAsync(path, false)
|
||||
.then((type) => (type == FileSystemEntityType.LINK._type));
|
||||
static Future<bool> isLink(String path) =>
|
||||
_getType(path, false).then((type) => (type == FileSystemEntityType.LINK));
|
||||
|
||||
/**
|
||||
* Checks if type(path) returns FileSystemEntityType.FILE.
|
||||
*/
|
||||
static Future<bool> isFile(String path) => _getTypeAsync(path, true)
|
||||
.then((type) => (type == FileSystemEntityType.FILE._type));
|
||||
static Future<bool> isFile(String path) =>
|
||||
_getType(path, true).then((type) => (type == FileSystemEntityType.FILE));
|
||||
|
||||
/**
|
||||
* Checks if type(path) returns FileSystemEntityType.DIRECTORY.
|
||||
*/
|
||||
static Future<bool> isDirectory(String path) => _getTypeAsync(path, true)
|
||||
.then((type) => (type == FileSystemEntityType.DIRECTORY._type));
|
||||
static Future<bool> isDirectory(String path) => _getType(path, true)
|
||||
.then((type) => (type == FileSystemEntityType.DIRECTORY));
|
||||
|
||||
/**
|
||||
* Synchronously checks if typeSync(path, followLinks: false) returns
|
||||
* FileSystemEntityType.LINK.
|
||||
*/
|
||||
static bool isLinkSync(String path) =>
|
||||
(_getTypeSync(path, false) == FileSystemEntityType.LINK._type);
|
||||
(_getTypeSync(path, false) == FileSystemEntityType.LINK);
|
||||
|
||||
/**
|
||||
* Synchronously checks if typeSync(path) returns
|
||||
* FileSystemEntityType.FILE.
|
||||
*/
|
||||
static bool isFileSync(String path) =>
|
||||
(_getTypeSync(path, true) == FileSystemEntityType.FILE._type);
|
||||
(_getTypeSync(path, true) == FileSystemEntityType.FILE);
|
||||
|
||||
/**
|
||||
* Synchronously checks if typeSync(path) returns
|
||||
* FileSystemEntityType.DIRECTORY.
|
||||
*/
|
||||
static bool isDirectorySync(String path) =>
|
||||
(_getTypeSync(path, true) == FileSystemEntityType.DIRECTORY._type);
|
||||
(_getTypeSync(path, true) == FileSystemEntityType.DIRECTORY);
|
||||
|
||||
external static _getType(_Namespace namespace, String path, bool followLinks);
|
||||
external static _identical(_Namespace namespace, String path1, String path2);
|
||||
external static _getTypeNative(
|
||||
_Namespace namespace, String path, bool followLinks);
|
||||
external static _identicalNative(
|
||||
_Namespace namespace, String path1, String path2);
|
||||
external static _resolveSymbolicLinks(_Namespace namespace, String path);
|
||||
|
||||
// Finds the next-to-last component when dividing at path separators.
|
||||
|
@ -619,9 +693,11 @@ abstract class FileSystemEntity {
|
|||
|
||||
/**
|
||||
* Removes the final path component of a path, using the platform's
|
||||
* path separator to split the path. Will not remove the root component
|
||||
* of a Windows path, like "C:\\" or "\\\\server_name\\".
|
||||
* Ignores trailing path separators, and leaves no trailing path separators.
|
||||
* path separator to split the path.
|
||||
*
|
||||
* Will not remove the root component of a Windows path, like "C:\\" or
|
||||
* "\\\\server_name\\". Ignores trailing path separators, and leaves no
|
||||
* trailing path separators.
|
||||
*/
|
||||
static String parentOf(String path) {
|
||||
int rootEnd = -1;
|
||||
|
@ -653,22 +729,40 @@ abstract class FileSystemEntity {
|
|||
*/
|
||||
Directory get parent => new Directory(parentOf(path));
|
||||
|
||||
static int _getTypeSync(String path, bool followLinks) {
|
||||
var result = _getType(_Namespace._namespace, path, followLinks);
|
||||
static FileSystemEntityType _getTypeSyncHelper(
|
||||
String path, bool followLinks) {
|
||||
var result = _getTypeNative(_Namespace._namespace, path, followLinks);
|
||||
_throwIfError(result, 'Error getting type of FileSystemEntity');
|
||||
return result;
|
||||
return FileSystemEntityType._lookup(result);
|
||||
}
|
||||
|
||||
static Future<int> _getTypeAsync(String path, bool followLinks) {
|
||||
static FileSystemEntityType _getTypeSync(String path, bool followLinks) {
|
||||
IoOverrides overrides = IoOverrides.current;
|
||||
if (overrides == null) {
|
||||
return _getTypeSyncHelper(path, followLinks);
|
||||
}
|
||||
return overrides.fseGetTypeSync(path, followLinks);
|
||||
}
|
||||
|
||||
static Future<FileSystemEntityType> _getTypeRequest(
|
||||
String path, bool followLinks) {
|
||||
return _File._dispatchWithNamespace(
|
||||
_FILE_TYPE, [null, path, followLinks]).then((response) {
|
||||
if (_isErrorResponse(response)) {
|
||||
throw _exceptionFromResponse(response, "Error getting type", path);
|
||||
}
|
||||
return response;
|
||||
return FileSystemEntityType._lookup(response);
|
||||
});
|
||||
}
|
||||
|
||||
static Future<FileSystemEntityType> _getType(String path, bool followLinks) {
|
||||
IoOverrides overrides = IoOverrides.current;
|
||||
if (overrides == null) {
|
||||
return _getTypeRequest(path, followLinks);
|
||||
}
|
||||
return overrides.fseGetType(path, followLinks);
|
||||
}
|
||||
|
||||
static _throwIfError(Object result, String msg, [String path]) {
|
||||
if (result is OSError) {
|
||||
throw new FileSystemException(msg, path, result);
|
||||
|
@ -750,8 +844,10 @@ class FileSystemEvent {
|
|||
final int type;
|
||||
|
||||
/**
|
||||
* The path that triggered the event. Depending on the platform and the
|
||||
* FileSystemEntity, the path may be relative.
|
||||
* The path that triggered the event.
|
||||
*
|
||||
* Depending on the platform and the FileSystemEntity, the path may be
|
||||
* relative.
|
||||
*/
|
||||
final String path;
|
||||
|
||||
|
|
|
@ -220,6 +220,7 @@ part 'io_sink.dart';
|
|||
part 'io_service.dart';
|
||||
part 'link.dart';
|
||||
part 'namespace_impl.dart';
|
||||
part 'overrides.dart';
|
||||
part 'platform.dart';
|
||||
part 'platform_impl.dart';
|
||||
part 'process.dart';
|
||||
|
|
|
@ -21,6 +21,7 @@ io_sdk_sources = [
|
|||
"io_service.dart",
|
||||
"link.dart",
|
||||
"namespace_impl.dart",
|
||||
"overrides.dart",
|
||||
"platform.dart",
|
||||
"platform_impl.dart",
|
||||
"process.dart",
|
||||
|
|
|
@ -12,7 +12,13 @@ abstract class Link implements FileSystemEntity {
|
|||
/**
|
||||
* Creates a Link object.
|
||||
*/
|
||||
factory Link(String path) => new _Link(path);
|
||||
factory Link(String path) {
|
||||
final IoOverrides overrides = IoOverrides.current;
|
||||
if (overrides == null) {
|
||||
return new _Link(path);
|
||||
}
|
||||
return overrides.createLink(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a [Link] object.
|
||||
|
|
395
sdk/lib/io/overrides.dart
Normal file
395
sdk/lib/io/overrides.dart
Normal file
|
@ -0,0 +1,395 @@
|
|||
// 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 = new Object();
|
||||
|
||||
const _asyncRunZoned = runZoned;
|
||||
|
||||
/// This class facilitates 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:
|
||||
///
|
||||
/// ```
|
||||
/// class MyDirectory implements Directory {
|
||||
/// ...
|
||||
/// // An implementation of the Directory interface
|
||||
/// ...
|
||||
/// }
|
||||
///
|
||||
/// 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 class IoOverrides {
|
||||
static IoOverrides get current {
|
||||
return Zone.current[_ioOverridesToken];
|
||||
}
|
||||
|
||||
/// 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>(R body(),
|
||||
{
|
||||
// Directory
|
||||
Directory Function(String) createDirectory,
|
||||
Directory Function() getCurrentDirectory,
|
||||
void Function(String) setCurrentDirectory,
|
||||
Directory Function() getSystemTempDirectory,
|
||||
|
||||
// File
|
||||
File Function(String) createFile,
|
||||
|
||||
// FileStat
|
||||
Future<FileStat> Function(String) stat,
|
||||
FileStat Function(String) statSync,
|
||||
|
||||
// FileSystemEntity
|
||||
Future<bool> Function(String, String) fseIdentical,
|
||||
bool Function(String, String) fseIdenticalSync,
|
||||
Future<FileSystemEntityType> Function(String, bool) fseGetType,
|
||||
FileSystemEntityType Function(String, bool) fseGetTypeSync,
|
||||
|
||||
// _FileSystemWatcher
|
||||
Stream<FileSystemEvent> Function(String, int, bool) fsWatch,
|
||||
bool Function() fsWatchIsSupported,
|
||||
|
||||
// Link
|
||||
Link Function(String) createLink,
|
||||
|
||||
// Optional Zone parameters
|
||||
ZoneSpecification zoneSpecification,
|
||||
Function onError}) {
|
||||
IoOverrides overrides = new _IoOverridesScope(
|
||||
// Directory
|
||||
createDirectory,
|
||||
getCurrentDirectory,
|
||||
setCurrentDirectory,
|
||||
getSystemTempDirectory,
|
||||
|
||||
// File
|
||||
createFile,
|
||||
|
||||
// FileStat
|
||||
stat,
|
||||
statSync,
|
||||
|
||||
// FileSystemEntity
|
||||
fseIdentical,
|
||||
fseIdenticalSync,
|
||||
fseGetType,
|
||||
fseGetTypeSync,
|
||||
|
||||
// _FileSystemWatcher
|
||||
fsWatch,
|
||||
fsWatchIsSupported,
|
||||
|
||||
// Link
|
||||
createLink,
|
||||
);
|
||||
return _asyncRunZoned<R>(body,
|
||||
zoneValues: {_ioOverridesToken: overrides},
|
||||
zoneSpecification: zoneSpecification,
|
||||
onError: onError);
|
||||
}
|
||||
|
||||
/// 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>(R body(), IoOverrides overrides,
|
||||
{ZoneSpecification zoneSpecification, Function onError}) {
|
||||
return _asyncRunZoned<R>(body,
|
||||
zoneValues: {_ioOverridesToken: overrides},
|
||||
zoneSpecification: zoneSpecification,
|
||||
onError: onError);
|
||||
}
|
||||
|
||||
// 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) => new _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) => new _File(path);
|
||||
|
||||
// FileStat
|
||||
|
||||
/// Asynchronously returns [FileStat] information for [path].
|
||||
///
|
||||
/// When this override is installed, this function overrides the behavior of
|
||||
/// `FileStat.stat()`.
|
||||
Future<FileStat> 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<bool> 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<FileSystemEntityType> fseGetType(String path, bool followLinks) {
|
||||
return FileSystemEntity._getTypeRequest(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(path, followLinks);
|
||||
}
|
||||
|
||||
// _FileSystemWatcher
|
||||
|
||||
/// Returns a [Stream] of [FileSystemEvent]s.
|
||||
///
|
||||
/// When this override is installed, this function overrides the behavior of
|
||||
/// `FileSystemEntity.watch()`.
|
||||
Stream<FileSystemEvent> 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) => new _Link(path);
|
||||
}
|
||||
|
||||
class _IoOverridesScope extends IoOverrides {
|
||||
final IoOverrides _previous = IoOverrides.current;
|
||||
|
||||
// Directory
|
||||
Directory Function(String) _createDirectory;
|
||||
Directory Function() _getCurrentDirectory;
|
||||
void Function(String) _setCurrentDirectory;
|
||||
Directory Function() _getSystemTempDirectory;
|
||||
|
||||
// File
|
||||
File Function(String) _createFile;
|
||||
|
||||
// FileStat
|
||||
Future<FileStat> Function(String) _stat;
|
||||
FileStat Function(String) _statSync;
|
||||
|
||||
// FileSystemEntity
|
||||
Future<bool> Function(String, String) _fseIdentical;
|
||||
bool Function(String, String) _fseIdenticalSync;
|
||||
Future<FileSystemEntityType> Function(String, bool) _fseGetType;
|
||||
FileSystemEntityType Function(String, bool) _fseGetTypeSync;
|
||||
|
||||
// _FileSystemWatcher
|
||||
Stream<FileSystemEvent> Function(String, int, bool) _fsWatch;
|
||||
bool Function() _fsWatchIsSupported;
|
||||
|
||||
// Link
|
||||
Link Function(String) _createLink;
|
||||
|
||||
_IoOverridesScope(
|
||||
// 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,
|
||||
);
|
||||
|
||||
// Directory
|
||||
@override
|
||||
Directory createDirectory(String path) {
|
||||
if (_createDirectory != null) return _createDirectory(path);
|
||||
if (_previous != null) return _previous.createDirectory(path);
|
||||
return super.createDirectory(path);
|
||||
}
|
||||
|
||||
@override
|
||||
Directory getCurrentDirectory() {
|
||||
if (_getCurrentDirectory != null) return _getCurrentDirectory();
|
||||
if (_previous != null) return _previous.getCurrentDirectory();
|
||||
return super.getCurrentDirectory();
|
||||
}
|
||||
|
||||
@override
|
||||
void setCurrentDirectory(String path) {
|
||||
if (_setCurrentDirectory != null)
|
||||
_setCurrentDirectory(path);
|
||||
else if (_previous != null)
|
||||
_previous.setCurrentDirectory(path);
|
||||
else
|
||||
super.setCurrentDirectory(path);
|
||||
}
|
||||
|
||||
@override
|
||||
Directory getSystemTempDirectory() {
|
||||
if (_getSystemTempDirectory != null) return _getSystemTempDirectory();
|
||||
if (_previous != null) return _previous.getSystemTempDirectory();
|
||||
return super.getSystemTempDirectory();
|
||||
}
|
||||
|
||||
// File
|
||||
@override
|
||||
File createFile(String path) {
|
||||
if (_createFile != null) return _createFile(path);
|
||||
if (_previous != null) return _previous.createFile(path);
|
||||
return super.createFile(path);
|
||||
}
|
||||
|
||||
// FileStat
|
||||
@override
|
||||
Future<FileStat> stat(String path) {
|
||||
if (_stat != null) return _stat(path);
|
||||
if (_previous != null) return _previous.stat(path);
|
||||
return super.stat(path);
|
||||
}
|
||||
|
||||
@override
|
||||
FileStat statSync(String path) {
|
||||
if (_stat != null) return _statSync(path);
|
||||
if (_previous != null) return _previous.statSync(path);
|
||||
return super.statSync(path);
|
||||
}
|
||||
|
||||
// FileSystemEntity
|
||||
@override
|
||||
Future<bool> fseIdentical(String path1, String path2) {
|
||||
if (_fseIdentical != null) return _fseIdentical(path1, path2);
|
||||
if (_previous != null) return _previous.fseIdentical(path1, path2);
|
||||
return super.fseIdentical(path1, path2);
|
||||
}
|
||||
|
||||
@override
|
||||
bool fseIdenticalSync(String path1, String path2) {
|
||||
if (_fseIdenticalSync != null) return _fseIdenticalSync(path1, path2);
|
||||
if (_previous != null) return _previous.fseIdenticalSync(path1, path2);
|
||||
return super.fseIdenticalSync(path1, path2);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<FileSystemEntityType> fseGetType(String path, bool followLinks) {
|
||||
if (_fseGetType != null) return _fseGetType(path, followLinks);
|
||||
if (_previous != null) return _previous.fseGetType(path, followLinks);
|
||||
return super.fseGetType(path, followLinks);
|
||||
}
|
||||
|
||||
@override
|
||||
FileSystemEntityType fseGetTypeSync(String path, bool followLinks) {
|
||||
if (_fseGetTypeSync != null) return _fseGetTypeSync(path, followLinks);
|
||||
if (_previous != null) return _previous.fseGetTypeSync(path, followLinks);
|
||||
return super.fseGetTypeSync(path, followLinks);
|
||||
}
|
||||
|
||||
// _FileSystemWatcher
|
||||
@override
|
||||
Stream<FileSystemEvent> fsWatch(String path, int events, bool recursive) {
|
||||
if (_fsWatch != null) return _fsWatch(path, events, recursive);
|
||||
if (_previous != null) return _previous.fsWatch(path, events, recursive);
|
||||
return super.fsWatch(path, events, recursive);
|
||||
}
|
||||
|
||||
bool fsWatchIsSupported() {
|
||||
if (_fsWatchIsSupported != null) return _fsWatchIsSupported();
|
||||
if (_previous != null) return _previous.fsWatchIsSupported();
|
||||
return super.fsWatchIsSupported();
|
||||
}
|
||||
|
||||
// Link
|
||||
@override
|
||||
Link createLink(String path) {
|
||||
if (_createLink != null) return _createLink(path);
|
||||
if (_previous != null) return _previous.createLink(path);
|
||||
return super.createLink(path);
|
||||
}
|
||||
}
|
178
tests/standalone/io/http_override_test.dart
Normal file
178
tests/standalone/io/http_override_test.dart
Normal file
|
@ -0,0 +1,178 @@
|
|||
// 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.
|
||||
|
||||
import 'dart:io';
|
||||
|
||||
import "package:expect/expect.dart";
|
||||
|
||||
class MyHttpClient1 implements HttpClient {
|
||||
String userAgent = "MyHttpClient1";
|
||||
|
||||
MyHttpClient1(SecurityContext context);
|
||||
|
||||
Duration idleTimeout;
|
||||
int maxConnectionsPerHost;
|
||||
bool autoUncompress;
|
||||
|
||||
Future<HttpClientRequest> open(
|
||||
String method, String host, int port, String path) =>
|
||||
null;
|
||||
Future<HttpClientRequest> openUrl(String method, Uri url) => null;
|
||||
Future<HttpClientRequest> get(String host, int port, String path) => null;
|
||||
Future<HttpClientRequest> getUrl(Uri url) => null;
|
||||
Future<HttpClientRequest> post(String host, int port, String path) => null;
|
||||
Future<HttpClientRequest> postUrl(Uri url) => null;
|
||||
Future<HttpClientRequest> put(String host, int port, String path) => null;
|
||||
Future<HttpClientRequest> putUrl(Uri url) => null;
|
||||
Future<HttpClientRequest> delete(String host, int port, String path) => null;
|
||||
Future<HttpClientRequest> deleteUrl(Uri url) => null;
|
||||
Future<HttpClientRequest> patch(String host, int port, String path) => null;
|
||||
Future<HttpClientRequest> patchUrl(Uri url) => null;
|
||||
Future<HttpClientRequest> head(String host, int port, String path) => null;
|
||||
Future<HttpClientRequest> headUrl(Uri url) => null;
|
||||
set authenticate(Future<bool> f(Uri url, String scheme, String realm)) {}
|
||||
void addCredentials(
|
||||
Uri url, String realm, HttpClientCredentials credentials) {}
|
||||
set findProxy(String f(Uri url)) {}
|
||||
set authenticateProxy(
|
||||
Future<bool> f(String host, int port, String scheme, String realm)) {}
|
||||
void addProxyCredentials(
|
||||
String host, int port, String realm, HttpClientCredentials credentials) {}
|
||||
set badCertificateCallback(
|
||||
bool callback(X509Certificate cert, String host, int port)) {}
|
||||
void close({bool force: false}) {}
|
||||
}
|
||||
|
||||
class MyHttpClient2 implements HttpClient {
|
||||
String userAgent = "MyHttpClient2";
|
||||
|
||||
MyHttpClient2(SecurityContext context);
|
||||
|
||||
Duration idleTimeout;
|
||||
int maxConnectionsPerHost;
|
||||
bool autoUncompress;
|
||||
|
||||
Future<HttpClientRequest> open(
|
||||
String method, String host, int port, String path) =>
|
||||
null;
|
||||
Future<HttpClientRequest> openUrl(String method, Uri url) => null;
|
||||
Future<HttpClientRequest> get(String host, int port, String path) => null;
|
||||
Future<HttpClientRequest> getUrl(Uri url) => null;
|
||||
Future<HttpClientRequest> post(String host, int port, String path) => null;
|
||||
Future<HttpClientRequest> postUrl(Uri url) => null;
|
||||
Future<HttpClientRequest> put(String host, int port, String path) => null;
|
||||
Future<HttpClientRequest> putUrl(Uri url) => null;
|
||||
Future<HttpClientRequest> delete(String host, int port, String path) => null;
|
||||
Future<HttpClientRequest> deleteUrl(Uri url) => null;
|
||||
Future<HttpClientRequest> patch(String host, int port, String path) => null;
|
||||
Future<HttpClientRequest> patchUrl(Uri url) => null;
|
||||
Future<HttpClientRequest> head(String host, int port, String path) => null;
|
||||
Future<HttpClientRequest> headUrl(Uri url) => null;
|
||||
set authenticate(Future<bool> f(Uri url, String scheme, String realm)) {}
|
||||
void addCredentials(
|
||||
Uri url, String realm, HttpClientCredentials credentials) {}
|
||||
set findProxy(String f(Uri url)) {}
|
||||
set authenticateProxy(
|
||||
Future<bool> f(String host, int port, String scheme, String realm)) {}
|
||||
void addProxyCredentials(
|
||||
String host, int port, String realm, HttpClientCredentials credentials) {}
|
||||
set badCertificateCallback(
|
||||
bool callback(X509Certificate cert, String host, int port)) {}
|
||||
void close({bool force: false}) {}
|
||||
}
|
||||
|
||||
class MyHttpOverrides extends HttpOverrides {
|
||||
@override
|
||||
HttpClient createHttpClient(SecurityContext context) {
|
||||
return new MyHttpClient1(context);
|
||||
}
|
||||
}
|
||||
|
||||
HttpClient myCreateHttp1Client(SecurityContext context) {
|
||||
return new MyHttpClient1(context);
|
||||
}
|
||||
|
||||
HttpClient myCreateHttp2Client(SecurityContext context) {
|
||||
return new MyHttpClient2(context);
|
||||
}
|
||||
|
||||
String myFindProxyFromEnvironment(Uri url, Map<String, String> environment) {
|
||||
return "proxy";
|
||||
}
|
||||
|
||||
withHttpOverridesTest() {
|
||||
HttpOverrides.runZoned(() {
|
||||
var httpClient = new HttpClient();
|
||||
Expect.isNotNull(httpClient);
|
||||
Expect.isTrue(httpClient is MyHttpClient1);
|
||||
Expect.equals((new MyHttpClient1(null)).userAgent, httpClient.userAgent);
|
||||
}, createHttpClient: myCreateHttp1Client);
|
||||
var httpClient = new HttpClient();
|
||||
Expect.isTrue(httpClient is HttpClient);
|
||||
Expect.isTrue(httpClient is! MyHttpClient1);
|
||||
}
|
||||
|
||||
nestedWithHttpOverridesTest() {
|
||||
HttpOverrides.runZoned(() {
|
||||
var httpClient = new HttpClient();
|
||||
Expect.isNotNull(httpClient);
|
||||
Expect.isTrue(httpClient is MyHttpClient1);
|
||||
Expect.equals((new MyHttpClient1(null)).userAgent, httpClient.userAgent);
|
||||
HttpOverrides.runZoned(() {
|
||||
var httpClient = new HttpClient();
|
||||
Expect.isNotNull(httpClient);
|
||||
Expect.isTrue(httpClient is MyHttpClient2);
|
||||
Expect.equals((new MyHttpClient2(null)).userAgent, httpClient.userAgent);
|
||||
}, createHttpClient: myCreateHttp2Client);
|
||||
httpClient = new HttpClient();
|
||||
Expect.isNotNull(httpClient);
|
||||
Expect.isTrue(httpClient is MyHttpClient1);
|
||||
Expect.equals((new MyHttpClient1(null)).userAgent, httpClient.userAgent);
|
||||
}, createHttpClient: myCreateHttp1Client);
|
||||
var httpClient = new HttpClient();
|
||||
Expect.isTrue(httpClient is HttpClient);
|
||||
Expect.isTrue(httpClient is! MyHttpClient1);
|
||||
Expect.isTrue(httpClient is! MyHttpClient2);
|
||||
}
|
||||
|
||||
nestedDifferentOverridesTest() {
|
||||
HttpOverrides.runZoned(() {
|
||||
var httpClient = new HttpClient();
|
||||
Expect.isNotNull(httpClient);
|
||||
Expect.isTrue(httpClient is MyHttpClient1);
|
||||
Expect.equals((new MyHttpClient1(null)).userAgent, httpClient.userAgent);
|
||||
HttpOverrides.runZoned(() {
|
||||
var httpClient = new HttpClient();
|
||||
Expect.isNotNull(httpClient);
|
||||
Expect.isTrue(httpClient is MyHttpClient1);
|
||||
Expect.equals((new MyHttpClient1(null)).userAgent, httpClient.userAgent);
|
||||
Expect.equals(myFindProxyFromEnvironment(null, null),
|
||||
HttpClient.findProxyFromEnvironment(null));
|
||||
}, findProxyFromEnvironment: myFindProxyFromEnvironment);
|
||||
httpClient = new HttpClient();
|
||||
Expect.isNotNull(httpClient);
|
||||
Expect.isTrue(httpClient is MyHttpClient1);
|
||||
Expect.equals((new MyHttpClient1(null)).userAgent, httpClient.userAgent);
|
||||
}, createHttpClient: myCreateHttp1Client);
|
||||
var httpClient = new HttpClient();
|
||||
Expect.isTrue(httpClient is HttpClient);
|
||||
Expect.isTrue(httpClient is! MyHttpClient1);
|
||||
Expect.isTrue(httpClient is! MyHttpClient2);
|
||||
}
|
||||
|
||||
zonedWithHttpOverridesTest() {
|
||||
HttpOverrides.runWithHttpOverrides(() {
|
||||
var httpClient = new HttpClient();
|
||||
Expect.isNotNull(httpClient);
|
||||
Expect.isTrue(httpClient is MyHttpClient1);
|
||||
Expect.equals((new MyHttpClient1(null)).userAgent, httpClient.userAgent);
|
||||
}, new MyHttpOverrides());
|
||||
}
|
||||
|
||||
main() {
|
||||
withHttpOverridesTest();
|
||||
nestedWithHttpOverridesTest();
|
||||
nestedDifferentOverridesTest();
|
||||
zonedWithHttpOverridesTest();
|
||||
}
|
193
tests/standalone/io/io_override_test.dart
Normal file
193
tests/standalone/io/io_override_test.dart
Normal file
|
@ -0,0 +1,193 @@
|
|||
// 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.
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import "package:expect/expect.dart";
|
||||
|
||||
class DirectoryMock extends FileSystemEntity implements Directory {
|
||||
final String path = "/mockdir";
|
||||
|
||||
DirectoryMock(String path);
|
||||
|
||||
static DirectoryMock createDirectory(String path) => new DirectoryMock(path);
|
||||
static DirectoryMock getCurrent() => new DirectoryMock(null);
|
||||
static void setCurrent(String path) {}
|
||||
static DirectoryMock getSystemTemp() => new DirectoryMock(null);
|
||||
|
||||
Uri get uri => null;
|
||||
Future<Directory> create({bool recursive: false}) => null;
|
||||
void createSync({bool recursive: false}) {}
|
||||
Future<Directory> createTemp([String prefix]) => null;
|
||||
Directory createTempSync([String prefix]) => null;
|
||||
Future<String> resolveSymbolicLinks() => null;
|
||||
String resolveSymbolicLinksSync() => null;
|
||||
Future<Directory> rename(String newPath) => null;
|
||||
Directory renameSync(String newPath) => null;
|
||||
Directory get absolute => null;
|
||||
Stream<FileSystemEntity> list(
|
||||
{bool recursive: false, bool followLinks: true}) =>
|
||||
null;
|
||||
List<FileSystemEntity> listSync(
|
||||
{bool recursive: false, bool followLinks: true}) =>
|
||||
null;
|
||||
}
|
||||
|
||||
class FileMock extends FileSystemEntity implements File {
|
||||
String get path => "/mockfile";
|
||||
|
||||
FileMock(String path);
|
||||
|
||||
static FileMock createFile(String path) => new FileMock(path);
|
||||
|
||||
Future<File> create({bool recursive: false}) => null;
|
||||
void createSync({bool recursive: false}) {}
|
||||
Future<File> rename(String newPath) => null;
|
||||
File renameSync(String newPath) => null;
|
||||
Future<File> copy(String newPath) => null;
|
||||
File copySync(String newPath) => null;
|
||||
Future<int> length() => null;
|
||||
int lengthSync() => null;
|
||||
File get absolute => null;
|
||||
Future<DateTime> lastAccessed() => null;
|
||||
DateTime lastAccessedSync() => null;
|
||||
Future setLastAccessed(DateTime time) => null;
|
||||
void setLastAccessedSync(DateTime time) {}
|
||||
Future<DateTime> lastModified() => null;
|
||||
DateTime lastModifiedSync() => null;
|
||||
Future setLastModified(DateTime time) => null;
|
||||
void setLastModifiedSync(DateTime time) {}
|
||||
Future<RandomAccessFile> open({FileMode mode: FileMode.READ}) => null;
|
||||
RandomAccessFile openSync({FileMode mode: FileMode.READ}) => null;
|
||||
Stream<List<int>> openRead([int start, int end]) => null;
|
||||
IOSink openWrite({FileMode mode: FileMode.WRITE, Encoding encoding: UTF8}) =>
|
||||
null;
|
||||
Future<List<int>> readAsBytes() => null;
|
||||
List<int> readAsBytesSync() => null;
|
||||
Future<String> readAsString({Encoding encoding: UTF8}) => null;
|
||||
String readAsStringSync({Encoding encoding: UTF8}) => null;
|
||||
Future<List<String>> readAsLines({Encoding encoding: UTF8}) => null;
|
||||
List<String> readAsLinesSync({Encoding encoding: UTF8}) => null;
|
||||
Future<File> writeAsBytes(List<int> bytes,
|
||||
{FileMode mode: FileMode.WRITE, bool flush: false}) =>
|
||||
null;
|
||||
void writeAsBytesSync(List<int> bytes,
|
||||
{FileMode mode: FileMode.WRITE, bool flush: false}) {}
|
||||
Future<File> writeAsString(String contents,
|
||||
{FileMode mode: FileMode.WRITE,
|
||||
Encoding encoding: UTF8,
|
||||
bool flush: false}) =>
|
||||
null;
|
||||
void writeAsStringSync(String contents,
|
||||
{FileMode mode: FileMode.WRITE,
|
||||
Encoding encoding: UTF8,
|
||||
bool flush: false}) {}
|
||||
}
|
||||
|
||||
class FileStatMock implements FileStat {
|
||||
final DateTime changed;
|
||||
final DateTime modified;
|
||||
final DateTime accessed;
|
||||
final FileSystemEntityType type;
|
||||
final int mode;
|
||||
final int size;
|
||||
|
||||
FileStatMock();
|
||||
|
||||
static Future<FileStat> stat(String path) {
|
||||
return new Future.value(new FileStatMock());
|
||||
}
|
||||
|
||||
static FileStat statSync(String path) => new FileStatMock();
|
||||
|
||||
String modeString() => null;
|
||||
}
|
||||
|
||||
class FileSystemEntityMock {
|
||||
static Future<bool> identical(String path1, String path2) {
|
||||
return new Future.value(false);
|
||||
}
|
||||
|
||||
static bool identicalSync(String path1, String path2) => false;
|
||||
|
||||
static Future<FileSystemEntityType> getType(String path, bool followLinks) {
|
||||
return new Future.value(FileSystemEntityType.FILE);
|
||||
}
|
||||
|
||||
static FileSystemEntityType getTypeSync(String path, bool followLinks) {
|
||||
return FileSystemEntityType.FILE;
|
||||
}
|
||||
}
|
||||
|
||||
class FileSystemWatcherMock {
|
||||
static Stream<FileSystemEvent> watch(
|
||||
String path, int events, bool recursive) {
|
||||
return null;
|
||||
}
|
||||
|
||||
static bool watchSupported() => false;
|
||||
}
|
||||
|
||||
class LinkMock extends FileSystemEntity implements Link {
|
||||
LinkMock(String path);
|
||||
|
||||
static createLink(String path) => new LinkMock(path);
|
||||
|
||||
Future<Link> create(String target, {bool recursive: false}) => null;
|
||||
void createSync(String target, {bool recursive: false}) {}
|
||||
void updateSync(String target) {}
|
||||
Future<Link> update(String target) => null;
|
||||
Future<String> resolveSymbolicLinks() => null;
|
||||
String resolveSymbolicLinksSync() => null;
|
||||
Future<Link> rename(String newPath) => null;
|
||||
Link renameSync(String newPath) => null;
|
||||
Link get absolute => null;
|
||||
Future<String> target() => null;
|
||||
String targetSync() => null;
|
||||
}
|
||||
|
||||
Future<Null> ioOverridesRunTest() async {
|
||||
Future<Null> f = IoOverrides.runZoned(
|
||||
() async {
|
||||
Expect.isTrue(new Directory("directory") is DirectoryMock);
|
||||
Expect.isTrue(Directory.current is DirectoryMock);
|
||||
Expect.isTrue(Directory.systemTemp is DirectoryMock);
|
||||
Expect.isTrue(new File("file") is FileMock);
|
||||
Expect.isTrue(await FileStat.stat("file") is FileStatMock);
|
||||
Expect.isTrue(FileStat.statSync("file") is FileStatMock);
|
||||
Expect.isFalse(await FileSystemEntity.identical("file", "file"));
|
||||
Expect.isFalse(FileSystemEntity.identicalSync("file", "file"));
|
||||
Expect.equals(
|
||||
await FileSystemEntity.type("file"), FileSystemEntityType.FILE);
|
||||
Expect.equals(
|
||||
FileSystemEntity.typeSync("file"), FileSystemEntityType.FILE);
|
||||
Expect.isFalse(FileSystemEntity.isWatchSupported);
|
||||
Expect.isNull(new Directory("directory").watch());
|
||||
Expect.isTrue(new Link("link") is LinkMock);
|
||||
},
|
||||
createDirectory: DirectoryMock.createDirectory,
|
||||
getCurrentDirectory: DirectoryMock.getCurrent,
|
||||
setCurrentDirectory: DirectoryMock.setCurrent,
|
||||
getSystemTempDirectory: DirectoryMock.getSystemTemp,
|
||||
createFile: FileMock.createFile,
|
||||
stat: FileStatMock.stat,
|
||||
statSync: FileStatMock.statSync,
|
||||
fseIdentical: FileSystemEntityMock.identical,
|
||||
fseIdenticalSync: FileSystemEntityMock.identicalSync,
|
||||
fseGetType: FileSystemEntityMock.getType,
|
||||
fseGetTypeSync: FileSystemEntityMock.getTypeSync,
|
||||
fsWatch: FileSystemWatcherMock.watch,
|
||||
fsWatchIsSupported: FileSystemWatcherMock.watchSupported,
|
||||
createLink: LinkMock.createLink,
|
||||
);
|
||||
Expect.isFalse(new Directory("directory") is DirectoryMock);
|
||||
Expect.isTrue(new Directory("directory") is Directory);
|
||||
await f;
|
||||
}
|
||||
|
||||
main() async {
|
||||
await ioOverridesRunTest();
|
||||
}
|
Loading…
Reference in a new issue