| // Copyright (c) 2024, 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:collection'; |
| import 'dart:convert'; |
| |
| import 'package:meta/meta.dart'; |
| |
| import '../platforms_impl.dart'; |
| import '../util/json_keys.dart' as json_key; |
| import 'zone_overrides.dart' as overrides; |
| |
| /// Fake Dart runtime platform information. |
| /// |
| /// Implements [Platform], but allows a [FakeBrowserPlatform] or |
| /// [FakeNativePlatform] to be the non-`null` platform object. |
| @visibleForTesting |
| final class FakePlatform extends PlatformTestBase { |
| /// The current native platform, if running on a native platform. |
| @override |
| final FakeNativePlatform? nativePlatform; |
| |
| /// The current browser platform, if running on a browser platform. |
| @override |
| final FakeBrowserPlatform? browserPlatform; |
| |
| /// Whether currently running on a native platform. |
| @override |
| bool get isNative => nativePlatform != null; |
| |
| /// Whether currently running in a browser. |
| @override |
| bool get isBrowser => browserPlatform != null; |
| |
| /// A fake platform with the given [nativePlatform]. |
| /// |
| /// Used by [FakeNativePlatform.run] to create a fake [Platform] |
| /// with itself as the [Platform.nativePlatform]. |
| const FakePlatform._native(FakeNativePlatform this.nativePlatform) |
| : browserPlatform = null; |
| |
| /// A fake platform with the given [browserPlatform]. |
| /// |
| /// Used by [FakeBrowserPlatform.run] to create a fake [Platform] |
| /// with itself as the [Platform.browserPlatform]. |
| const FakePlatform._browser(FakeBrowserPlatform this.browserPlatform) |
| : nativePlatform = null; |
| |
| /// A platform with *none* of the native or browser platforms set. |
| const FakePlatform.unknown() |
| : nativePlatform = null, |
| browserPlatform = null; |
| |
| /// A fake platform with the same properties as [platform]. |
| factory FakePlatform.fromPlatform(Platform platform) { |
| // There is currently no way to have a platform with both |
| // a browser platform and a native platform. |
| var native = platform.nativePlatform; |
| if (native != null) return FakePlatform.fromNative(native); |
| var browser = platform.browserPlatform; |
| if (browser != null) return FakePlatform.fromBrowser(browser); |
| return const FakePlatform.unknown(); |
| } |
| |
| /// Creates a [FakePlatform] with a [NativePlatform]. |
| /// |
| /// Creates a [FakeNativePlatform.new] with the same arguments, |
| /// and a [FakePlatform] with that as [nativePlatform]. |
| /// |
| /// It's recommended to use |
| /// ```dart |
| /// FakePlatform.fromNative(FakeNativePlatform(...)) |
| /// ``` |
| FakePlatform.native({ |
| Map<String, String>? environment, |
| String? executable, |
| List<String>? executableArguments, |
| String? lineTerminator, |
| String? localeName, |
| String? localHostname, |
| int? numberOfProcessors, |
| String? operatingSystem, |
| String? operatingSystemVersion, |
| String? packageConfig, |
| String? pathSeparator, |
| String? resolvedExecutable, |
| Uri? script, |
| bool? stdinSupportsAnsi, |
| bool? stdoutSupportsAnsi, |
| String? version, |
| }) : this._native(FakeNativePlatform( |
| environment: environment, |
| executable: executable, |
| executableArguments: executableArguments, |
| lineTerminator: lineTerminator, |
| localeName: localeName, |
| localHostname: localHostname, |
| numberOfProcessors: numberOfProcessors, |
| operatingSystem: operatingSystem, |
| operatingSystemVersion: operatingSystemVersion, |
| packageConfig: packageConfig, |
| pathSeparator: pathSeparator, |
| resolvedExecutable: resolvedExecutable, |
| script: script, |
| stdinSupportsAnsi: stdinSupportsAnsi, |
| stdoutSupportsAnsi: stdoutSupportsAnsi, |
| version: version, |
| )); |
| |
| /// Creates a [FakePlatform] with a [NativePlatform]. |
| /// |
| /// If [nativePlatform] is omitted or `null`, the created `FakePlatform] |
| /// has a new [FakeNativePlatform] as [Platform.nativePlatform]. |
| FakePlatform.fromNative(NativePlatform nativePlatform) |
| : this._native(FakeNativePlatform.from(nativePlatform)); |
| |
| /// Creates a [FakePlatform] with a [FakeNativePlatform] created from JSON. |
| /// |
| /// A new [FakeNativePlatform] is created using [FakeNativePlatform.fromJson] |
| /// with [nativePlatformJson] as argument, an a new [FakePlatform] is created |
| /// with that native platform as [Platform.nativePlatform]. |
| FakePlatform.nativeFromJson(String nativePlatformJson) |
| : this._native(FakeNativePlatform.fromJson(nativePlatformJson)); |
| |
| /// Creates a [FakePlatform] with a [BrowserPlatform]. |
| /// |
| /// If [browserPlatform] is omitted or `null`, the created `FakePlatform] |
| /// has a new [FakeBrowserPlatform] as [Platform.browserPlatform]. |
| FakePlatform.fromBrowser([BrowserPlatform? browserPlatform]) |
| : this._browser(browserPlatform = browserPlatform == null |
| ? FakeBrowserPlatform() |
| : FakeBrowserPlatform.fromPlatform(browserPlatform)); |
| |
| /// Creates a [FakePlatform] with a [FakeBrowserPlatform] created from JSON. |
| /// |
| /// A new [FakeBrowserPlatform] is created using |
| /// [FakeBrowserPlatform.fromJson] with [browserPlatformJson] as argument, |
| /// and a new [FakePlatform] is created with that native platform as |
| /// [Platform.browserPlatform]. |
| FakePlatform.browserFromJson(String browserPlatformJson) |
| : this._browser(FakeBrowserPlatform.fromJson(browserPlatformJson)); |
| |
| /// Runs [fakePlatformCode] with this fake platform as the current platform. |
| /// |
| /// While [fakePlatformCode] is running, the [Platform.current] refers to this |
| /// fake platform, which is likely the [FakePlatform.unknown] platform. |
| /// |
| /// Prior reads of [Platform.current] will retain their original value, |
| /// so the `fakePlatformCode` should make sure to read [Platform.current] |
| /// when it's needed, and avoid any caching. |
| R run<R>(R Function() fakePlatformCode) => |
| overrides.runWith(fakePlatformCode, this, _OverrideMarker.marker); |
| |
| /// Migration helper for legacy `FakePlatform.copyWith`. |
| /// |
| /// Use [FakeNativePlatform.copyWith] instead. |
| /// Work directly with [FakeNativePlatform], rather than creating a |
| /// `FakePlatform` from it. |
| /// |
| /// Only works if there is a current [nativePlatform]. |
| /// |
| /// Will be deprecated and removed when legacy classes are removed. |
| FakePlatform copyWithNativeMigrationHelper({ |
| int? numberOfProcessors, |
| String? pathSeparator, |
| String? operatingSystem, |
| String? operatingSystemVersion, |
| String? localHostname, |
| Map<String, String>? environment, |
| String? executable, |
| String? resolvedExecutable, |
| Uri? script, |
| List<String>? executableArguments, |
| String? packageConfig, |
| String? version, |
| bool? stdinSupportsAnsi, |
| bool? stdoutSupportsAnsi, |
| String? localeName, |
| }) { |
| return FakePlatform._native(nativePlatform!.copyWith( |
| numberOfProcessors: numberOfProcessors, |
| pathSeparator: pathSeparator, |
| operatingSystem: operatingSystem, |
| operatingSystemVersion: operatingSystemVersion, |
| localHostname: localHostname, |
| environment: environment, |
| executable: executable, |
| resolvedExecutable: resolvedExecutable, |
| script: script, |
| executableArguments: executableArguments, |
| packageConfig: packageConfig, |
| version: version, |
| stdinSupportsAnsi: stdinSupportsAnsi, |
| stdoutSupportsAnsi: stdoutSupportsAnsi, |
| localeName: localeName, |
| )); |
| } |
| } |
| |
| /// Instance used to mark overrides as used. |
| enum _OverrideMarker implements overrides.OverrideMarker { |
| marker; |
| } |
| |
| /// Fake [BrowserPlatform] for testing. |
| @visibleForTesting |
| final class FakeBrowserPlatform extends BrowserPlatformTestBase { |
| static const _className = 'FakeBrowserPlatform'; |
| |
| final String? _version; |
| final String? _userAgent; |
| |
| FakeBrowserPlatform({String? version, String? userAgent}) |
| : _version = version, |
| _userAgent = userAgent; |
| |
| factory FakeBrowserPlatform.fromJson(String jsonText) { |
| var json = jsonDecode(jsonText); |
| if (json is! Map<String, Object?>) { |
| throw FormatException('Not a JSON map', jsonText); |
| } |
| return FakeBrowserPlatform( |
| version: _getJsonProperty<String>(json, json_key.version, jsonText), |
| userAgent: _getJsonProperty<String>(json, json_key.userAgent, jsonText), |
| ); |
| } |
| |
| factory FakeBrowserPlatform.fromPlatform(BrowserPlatform platform) { |
| if (platform is FakeBrowserPlatform) { |
| return platform.copyWith(); |
| } |
| return FakeBrowserPlatform( |
| version: platform.version, |
| userAgent: platform.userAgent, |
| ); |
| } |
| |
| FakeBrowserPlatform copyWith({ |
| String? version, |
| String? userAgent, |
| }) => |
| FakeBrowserPlatform( |
| version: version ?? _version, |
| userAgent: userAgent ?? _userAgent, |
| ); |
| |
| /// Runs [fakePlatformCode] with this as the current browser platform. |
| /// |
| /// While [fakePlatformCode] is running, the [Platform.browserPlatform] |
| /// of [Platform.current] refers to this [FakeBrowserPlatform]. |
| /// |
| /// Prior reads of [Platform.current] will retain their original value, |
| /// so the `fakePlatformCode` should make sure to read [Platform.current] |
| /// when it's needed, and avoid any caching. |
| R run<R>(R Function() fakePlatformCode) => overrides.runWith( |
| fakePlatformCode, FakePlatform._browser(this), _OverrideMarker.marker); |
| |
| @override |
| String toJson() => const JsonEncoder.withIndent(' ').convert({ |
| if (_version != null) json_key.version: _version, |
| if (_userAgent != null) json_key.userAgent: _userAgent, |
| }); |
| |
| @override |
| String get userAgent => |
| _throwIfUnset(_userAgent, _className, json_key.userAgent); |
| |
| @override |
| String get version => _throwIfUnset(_version, _className, json_key.version); |
| } |
| |
| /// A fake [NativePlatform] for testing. |
| /// |
| /// The individual properties can be left unset by the constructors. |
| /// Accessing such properties will throw, but a test which doesn't use |
| /// those properties doesn't have to provide values for them. |
| /// |
| /// This class should not be used in production, since it reduces the |
| /// compiler's ability to predict the values of the |
| /// [NativePlatform.operatingSystem] or, for example, [NativePlatform.isLinux] |
| /// checks, which can lead to larger compiled programs. |
| @visibleForTesting |
| final class FakeNativePlatform extends NativePlatformTestBase { |
| static const String _className = 'FakeNativePlatform'; |
| |
| /// Operating system ID string, or `null` if not configured. |
| /// |
| /// Use one of the [NativePlatform.android] through [NativePlatform.windows] |
| /// constants. |
| final String? _operatingSystem; |
| final String? _operatingSystemVersion; |
| final Map<String, String>? _environment; |
| final String? _executable; |
| final List<String>? _executableArguments; |
| final String? _lineTerminator; |
| final String? _localeName; |
| final String? _localHostname; |
| final int? _numberOfProcessors; |
| final String? _pathSeparator; |
| @override |
| final String? packageConfig; |
| final String? _resolvedExecutable; |
| final Uri? _script; |
| final bool? _stdinSupportsAnsi; |
| final bool? _stdoutSupportsAnsi; |
| final String? _version; |
| |
| /// Creates a new [FakeNativePlatform] with the specified properties. |
| /// |
| /// Parameters that are not provided with a non-`null` value |
| /// will leave the corresponding property without a value. |
| /// |
| /// Reading, for example, [FakeNativePlatform.operatingSystem] |
| /// when it had not been given a value, will throw an error. |
| /// This behavior can be used in tests to ensure that code does not |
| /// read properties that it is not supposed to. |
| /// |
| /// The [operatingSystem] string must be one of the known operating system |
| /// ID strings in [NativePlatform.operatingSystemValues]. |
| FakeNativePlatform({ |
| Map<String, String>? environment, |
| String? executable, |
| List<String>? executableArguments, |
| String? lineTerminator, |
| String? localeName, |
| String? localHostname, |
| int? numberOfProcessors, |
| String? operatingSystem, |
| String? operatingSystemVersion, |
| this.packageConfig, |
| String? pathSeparator, |
| String? resolvedExecutable, |
| Uri? script, |
| bool? stdinSupportsAnsi, |
| bool? stdoutSupportsAnsi, |
| String? version, |
| }) : _environment = environment == null |
| ? null |
| : operatingSystem == NativePlatform.windows |
| ? (_caseInsensitiveMap()..addAll(environment)) |
| : environment, |
| _executable = executable, |
| _executableArguments = executableArguments, |
| _lineTerminator = lineTerminator, |
| _localeName = localeName, |
| _localHostname = localHostname, |
| _numberOfProcessors = numberOfProcessors, |
| _operatingSystem = operatingSystem, |
| _operatingSystemVersion = operatingSystemVersion, |
| _pathSeparator = pathSeparator, |
| _resolvedExecutable = resolvedExecutable, |
| _script = script, |
| _stdinSupportsAnsi = stdinSupportsAnsi, |
| _stdoutSupportsAnsi = stdoutSupportsAnsi, |
| _version = version; |
| |
| /// Creates a new [FakeNativePlatform] with properties from a JSON string. |
| /// |
| /// The [jsonText] must be a valid JSON string representing a JSON object, |
| /// and the values for the [NativePlatform] properties are extracted from |
| /// the object's values for keys with the properties names. |
| /// |
| /// Example: |
| /// ```dart |
| /// var fake = FakeNativePlatform.fromJson('''{ |
| /// "operatingSystem": "linux", |
| /// "operatingSystemVersion": "fakeVersion.2.3", |
| /// "numberOfProcessors": 42, |
| /// "lineTerminator": "\r", |
| /// "packageConfig": null |
| /// }''')' |
| /// ``` |
| /// The JSON object's value for such a keys must be either a string or `null`. |
| /// |
| /// Throws a [FormatException] if the [jsonText] string is not valid |
| /// JSON text, or the JSON text does not represent a JSON object. |
| /// The property values must have the correct type for the corresponding |
| /// [NativePlatform] property, or be `null`. |
| /// If the map value of a property key is not a string, integer, boolean, |
| /// list of strings, or map with string keys and values, |
| /// as required by the corresponding property, |
| /// a [FormatException] is also thrown. |
| /// |
| /// Other keys are ignored. Missing property keys, |
| /// or keys with a `null` value, leave the property undefined. |
| /// |
| /// The [toJson] method will leave out undefined properties, so |
| /// `FakeNativePlatform.fromJson(fakeNativePlatform.toJson())` can create |
| /// an exact copy at a later time. |
| /// (To create a copy _right now_, `fakeNativePlatform.copyWith()` |
| /// is easier and more efficient.) |
| factory FakeNativePlatform.fromJson(String jsonText) { |
| Object? jsonObject = jsonDecode(jsonText); |
| if (jsonObject is! Map<String, Object?>) { |
| throw FormatException('Not a JSON object', jsonText); |
| } |
| |
| // Read out the values that require special validation first. |
| var environment = _getJsonProperty<Map<String, Object?>>( |
| jsonObject, json_key.environment, jsonText); |
| var executableArguments = _getJsonProperty<List<Object?>>( |
| jsonObject, json_key.executableArguments, jsonText); |
| var script = _getJsonProperty<String>(jsonObject, 'script', jsonText); |
| |
| return FakeNativePlatform( |
| // Check that environment values are strings. Filter out null values. |
| environment: environment == null |
| ? null |
| : <String, String>{ |
| for (var MapEntry(:key, :value) in environment.entries) |
| if (value != null) |
| key: (value is String |
| ? value |
| : _failJsonParse<String>( |
| 'environment[$key]', value, jsonText)) |
| }, |
| executable: |
| _getJsonProperty<String>(jsonObject, json_key.executable, jsonText), |
| // Check the executable arguments are strings. |
| executableArguments: executableArguments == null |
| ? null |
| : <String>[ |
| for (var argument in executableArguments) |
| argument is String |
| ? argument |
| : _failJsonParse<String>( |
| 'executableArguments', argument, jsonText) |
| ], |
| lineTerminator: _getJsonProperty<String>( |
| jsonObject, json_key.lineTerminator, jsonText), |
| localeName: |
| _getJsonProperty<String>(jsonObject, json_key.localeName, jsonText), |
| localHostname: _getJsonProperty<String>( |
| jsonObject, json_key.localHostname, jsonText), |
| numberOfProcessors: _getJsonProperty<int>( |
| jsonObject, json_key.numberOfProcessors, jsonText), |
| operatingSystem: _getJsonProperty<String>( |
| jsonObject, json_key.operatingSystem, jsonText), |
| operatingSystemVersion: _getJsonProperty<String>( |
| jsonObject, json_key.operatingSystemVersion, jsonText), |
| packageConfig: _getJsonProperty<String>( |
| jsonObject, json_key.packageConfig, jsonText), |
| pathSeparator: _getJsonProperty<String>( |
| jsonObject, json_key.pathSeparator, jsonText), |
| resolvedExecutable: _getJsonProperty<String>( |
| jsonObject, json_key.resolvedExecutable, jsonText), |
| // Checks that the script is a valid URI. |
| script: script == null ? null : Uri.parse(script), |
| stdinSupportsAnsi: _getJsonProperty<bool>( |
| jsonObject, json_key.stdinSupportsAnsi, jsonText), |
| stdoutSupportsAnsi: _getJsonProperty<bool>( |
| jsonObject, json_key.stdoutSupportsAnsi, jsonText), |
| version: _getJsonProperty<String>(jsonObject, json_key.version, jsonText), |
| ); |
| } |
| |
| /// Creates a new [FakeNativePlatform] with the properties of [platform]. |
| factory FakeNativePlatform.from(NativePlatform platform) => |
| platform is FakeNativePlatform // Values may be unset. |
| ? platform.copyWith() |
| : FakeNativePlatform( |
| environment: <String, String>{...platform.environment}, |
| executable: platform.executable, |
| executableArguments: List.of(platform.executableArguments), |
| lineTerminator: platform.lineTerminator, |
| localeName: platform.localeName, |
| localHostname: platform.localHostname, |
| numberOfProcessors: platform.numberOfProcessors, |
| operatingSystem: platform.operatingSystem, |
| operatingSystemVersion: platform.operatingSystemVersion, |
| packageConfig: platform.packageConfig, |
| pathSeparator: platform.pathSeparator, |
| resolvedExecutable: platform.resolvedExecutable, |
| script: platform.script, |
| stdinSupportsAnsi: platform.stdinSupportsAnsi, |
| stdoutSupportsAnsi: platform.stdoutSupportsAnsi, |
| version: platform.version, |
| ); |
| |
| /// Whether the operating system is Android. |
| @override |
| bool get isAndroid => operatingSystem == NativePlatform.android; |
| |
| /// Whether the operating system is Fuchsia. |
| @override |
| bool get isFuchsia => operatingSystem == NativePlatform.fuchsia; |
| |
| /// Whether the operating system is iOS. |
| @override |
| bool get isIOS => operatingSystem == NativePlatform.iOS; |
| |
| /// Whether the operating system is Linux. |
| @override |
| bool get isLinux => operatingSystem == NativePlatform.linux; |
| |
| /// Whether the operating system is OS X. |
| @override |
| bool get isMacOS => operatingSystem == NativePlatform.macOS; |
| |
| /// Whether the operating system is Windows. |
| @override |
| bool get isWindows => operatingSystem == NativePlatform.windows; |
| |
| @override |
| String get operatingSystem => |
| _throwIfUnset(_operatingSystem, _className, json_key.operatingSystem); |
| |
| @override |
| String get operatingSystemVersion => _throwIfUnset( |
| _operatingSystemVersion, _className, json_key.operatingSystemVersion); |
| |
| @override |
| Map<String, String> get environment => |
| _throwIfUnset(_environment, _className, json_key.environment); |
| |
| @override |
| String get executable => |
| _throwIfUnset(_executable, _className, json_key.executable); |
| |
| @override |
| List<String> get executableArguments => _throwIfUnset<List<String>>( |
| _executableArguments, _className, json_key.executableArguments); |
| |
| @override |
| String get lineTerminator => |
| _throwIfUnset(_lineTerminator, _className, json_key.lineTerminator); |
| |
| @override |
| String get localeName => |
| _throwIfUnset(_localeName, _className, json_key.localeName); |
| |
| @override |
| String get localHostname => |
| _throwIfUnset(_localHostname, _className, json_key.localHostname); |
| |
| @override |
| int get numberOfProcessors => _throwIfUnset( |
| _numberOfProcessors, _className, json_key.numberOfProcessors); |
| |
| @override |
| String get pathSeparator => |
| _throwIfUnset(_pathSeparator, _className, json_key.pathSeparator); |
| |
| @override |
| String get resolvedExecutable => _throwIfUnset( |
| _resolvedExecutable, _className, json_key.resolvedExecutable); |
| |
| @override |
| Uri get script => _throwIfUnset(_script, _className, json_key.script); |
| |
| @override |
| bool get stdinSupportsAnsi => |
| _throwIfUnset(_stdinSupportsAnsi, _className, json_key.stdinSupportsAnsi); |
| |
| @override |
| bool get stdoutSupportsAnsi => _throwIfUnset( |
| _stdoutSupportsAnsi, _className, json_key.stdoutSupportsAnsi); |
| |
| @override |
| String get version => _throwIfUnset(_version, _className, json_key.version); |
| |
| /// Creates a new [FakeNativePlatform] from this one. |
| /// |
| /// If a parameter is given non-`null` argument value, |
| /// the created object will have that value for the corresponding property, |
| /// otherwise it will use this object's value of that property. |
| FakeNativePlatform copyWith({ |
| Map<String, String>? environment, |
| String? executable, |
| List<String>? executableArguments, |
| String? lineTerminator, |
| String? localeName, |
| String? localHostname, |
| int? numberOfProcessors, |
| String? operatingSystem, |
| String? operatingSystemVersion, |
| String? packageConfig, |
| String? pathSeparator, |
| String? resolvedExecutable, |
| Uri? script, |
| bool? stdinSupportsAnsi, |
| bool? stdoutSupportsAnsi, |
| String? version, |
| }) { |
| return FakeNativePlatform( |
| environment: environment ?? |
| (_environment == null ? null : <String, String>{..._environment}), |
| executable: executable ?? _executable, |
| executableArguments: |
| (executableArguments ?? _executableArguments)?.toList(), |
| lineTerminator: lineTerminator ?? _lineTerminator, |
| localeName: localeName ?? _localeName, |
| localHostname: localHostname ?? _localHostname, |
| numberOfProcessors: numberOfProcessors ?? _numberOfProcessors, |
| operatingSystem: operatingSystem ?? _operatingSystem, |
| operatingSystemVersion: operatingSystemVersion ?? _operatingSystemVersion, |
| packageConfig: packageConfig ?? this.packageConfig, |
| pathSeparator: pathSeparator ?? _pathSeparator, |
| resolvedExecutable: resolvedExecutable ?? _resolvedExecutable, |
| script: script ?? _script, |
| stdinSupportsAnsi: stdinSupportsAnsi ?? _stdinSupportsAnsi, |
| stdoutSupportsAnsi: stdoutSupportsAnsi ?? _stdoutSupportsAnsi, |
| version: version ?? _version, |
| ); |
| } |
| |
| /// Runs [fakePlatformCode] with this native platform as current platform. |
| /// |
| /// If [fakePlatformCode] reads [Platform.current], it gets a platform object |
| /// whose [Platform.nativePlatform] is this fake native platform. |
| R run<R>(R Function() fakePlatformCode) => overrides.runWith( |
| fakePlatformCode, FakePlatform._native(this), _OverrideMarker.marker); |
| |
| /// Wraps [withOverride] to run it with this fake native platform as current. |
| /// |
| /// The returned function runs [withOverride] through [run] every time it's |
| /// called, to ensure all the calls will see the same override. |
| R Function() bind<R>(R Function() withOverride) => () => run(withOverride); |
| |
| /// A JSON-encoded representation of this platform configuration. |
| /// |
| /// Unset values are not included. |
| /// |
| /// Can be parsed back by [FakeNativePlatform.fromJson]. |
| @override |
| String toJson() { |
| return const JsonEncoder.withIndent(' ').convert(<String, dynamic>{ |
| if (_environment != null) json_key.environment: _environment, |
| if (_executable != null) json_key.executable: _executable, |
| if (_executableArguments != null) |
| json_key.executableArguments: _executableArguments, |
| if (_lineTerminator != null) json_key.lineTerminator: _lineTerminator, |
| if (_localeName != null) json_key.localeName: _localeName, |
| if (_localHostname != null) json_key.localHostname: _localHostname, |
| if (_numberOfProcessors != null) |
| json_key.numberOfProcessors: _numberOfProcessors, |
| if (_operatingSystem != null) json_key.operatingSystem: _operatingSystem, |
| if (_operatingSystemVersion != null) |
| json_key.operatingSystemVersion: _operatingSystemVersion, |
| if (packageConfig != null) json_key.packageConfig: packageConfig, |
| if (_pathSeparator != null) json_key.pathSeparator: _pathSeparator, |
| if (_resolvedExecutable != null) |
| json_key.resolvedExecutable: _resolvedExecutable, |
| if (_script != null) json_key.script: _script.toString(), |
| if (_stdinSupportsAnsi != null) |
| json_key.stdinSupportsAnsi: _stdinSupportsAnsi, |
| if (_stdoutSupportsAnsi != null) |
| json_key.stdoutSupportsAnsi: _stdoutSupportsAnsi, |
| if (_version != null) json_key.version: _version, |
| }); |
| } |
| } |
| |
| /// Reports a failure to parse a JSON property at its expected type. |
| Never _failJsonParse<T>(String property, Object? value, String? source) { |
| throw FormatException('Property "$property" is not a $T: $value', source); |
| } |
| |
| /// Reads a property from a JSON map, and throws if it has the wrong type. |
| /// |
| /// Reads the [property] key from the [jsonMap] and returns the value, |
| /// if it has type `T?`. |
| /// |
| /// A `null` value is allowed and treated the same |
| /// as if the [property] key is absent in the [jsonMap]. |
| /// Only fails if the key exists and has a value of a different type. |
| T? _getJsonProperty<T extends Object>( |
| Map<String, Object?> jsonMap, String property, String? source) { |
| var value = jsonMap[property]; |
| if (value is T?) return value; |
| _failJsonParse<T>(property, value, source); |
| } |
| |
| /// Throws if [value] is `null`, otherwise returns it. |
| /// |
| /// Used by getters on fake classes like [FakeNativePlatform], |
| /// which allow otherwise non-nullable properties to be unset |
| /// as long as they are not read. |
| /// This function is called when reading such a property. |
| T _throwIfUnset<T extends Object>(T? value, String className, String name) => |
| value ?? (throw StateError('$className.$name property is unset')); |
| |
| /// A simple inefficient case-insensitive map. |
| /// |
| /// Used by [FakeNativePlatform.environment] when the (possibly fake) |
| /// operating system is [NativePlatform.windows]. |
| /// Performance is not a consideration since [FakeNativePlatform] |
| /// is only intended for testing, and the environment is not expected |
| /// to be larger than necessary. |
| Map<String, String> _caseInsensitiveMap() => HashMap<String, String>( |
| equals: (String a, String b) => |
| a == b || a.toLowerCase() == b.toLowerCase(), |
| hashCode: (String a) => a.toLowerCase().hashCode); |
| |
| /// Temporary name for [FakePlatform] to avoid name conflict. |
| /// |
| /// Rename any reference to [FakePlatform] after importing *only* |
| /// `testing.dart`, not also `platform.dart`. |
| /// |
| /// Since the legacy class `FakePlatform`, corresponding to the new |
| /// [FakeNativePlatform], has the same name as the new [FakePlatform], |
| /// both are available by an aliased name to allow migration. |
| /// |
| /// When the legacy declarations are removed, this type alias will |
| /// be deprecated too |
| typedef FakePlatformMigrationHelper = FakePlatform; |