diff --git a/CODEOWNERS b/CODEOWNERS index 21ef69fc0309..9a0ade20876a 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -30,6 +30,7 @@ packages/multicast_dns/** @dnfield packages/palette_generator/** @gspencergoog packages/path_provider/** @stuartmorgan packages/pigeon/** @tarrinneal +packages/platform/** @stuartmorgan packages/plugin_platform_interface/** @stuartmorgan packages/pointer_interceptor/** @ditman packages/quick_actions/** @bparrishMines diff --git a/README.md b/README.md index 8fdec7e1e856..75dbd9b01c16 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ These packages are also available on [pub](https://pub.dartlang.org/flutter/pack ## Issues Please file any issues, bugs, or feature requests in the [main flutter -repo](https://github.com/flutter/flutter/issues/new). +repo](https://github.com/flutter/flutter/issues/new). Issues pertaining to this repository are [labeled "package"](https://github.com/flutter/flutter/issues?q=is%3Aopen+is%3Aissue+label%3Apackage). @@ -64,6 +64,7 @@ These are the packages hosted in this repository: | [palette\_generator](./packages/palette_generator/) | [![pub package](https://img.shields.io/pub/v/palette_generator.svg)](https://pub.dartlang.org/packages/palette_generator) | [![pub points](https://img.shields.io/pub/points/palette_generator)](https://pub.dev/packages/palette_generator/score) | [![popularity](https://img.shields.io/pub/popularity/palette_generator)](https://pub.dev/packages/palette_generator/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20palette_generator?label=)](https://github.com/flutter/flutter/labels/p%3A%20palette_generator) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p:%20palette_generator?label=)](https://github.com/flutter/packages/labels/p%3A%20palette_generator) | | [path\_provider](./packages/path_provider/) | [![pub package](https://img.shields.io/pub/v/path_provider.svg)](https://pub.dev/packages/path_provider) | [![pub points](https://img.shields.io/pub/points/path_provider)](https://pub.dev/packages/path_provider/score) | [![popularity](https://img.shields.io/pub/popularity/path_provider)](https://pub.dev/packages/path_provider/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20path_provider?label=)](https://github.com/flutter/flutter/labels/p%3A%20path_provider) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p:%20path_provider?label=)](https://github.com/flutter/packages/labels/p%3A%20path_provider) | | [pigeon](./packages/pigeon/) | [![pub package](https://img.shields.io/pub/v/pigeon.svg)](https://pub.dev/packages/pigeon) | [![pub points](https://img.shields.io/pub/points/pigeon)](https://pub.dev/packages/pigeon/score) | [![popularity](https://img.shields.io/pub/popularity/pigeon)](https://pub.dev/packages/pigeon/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/pigeon?label=)](https://github.com/flutter/flutter/labels/pigeon) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p:%20pigeon?label=)](https://github.com/flutter/packages/labels/p%3A%20pigeon) | +| [platform](./packages/platform/) | [![pub package](https://img.shields.io/pub/v/platform.svg)](https://pub.dev/packages/platform) | [![pub points](https://img.shields.io/pub/points/platform)](https://pub.dev/packages/platform/score) | [![popularity](https://img.shields.io/pub/popularity/platform)](https://pub.dev/packages/platform/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20platform?label=)](https://github.com/flutter/flutter/labels/p%3A%20platform) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p:%20platform?label=)](https://github.com/flutter/packages/labels/p%3A%20platform) | | [pointer\_interceptor](./packages/pointer_interceptor/) | [![pub package](https://img.shields.io/pub/v/pointer_interceptor.svg)](https://pub.dev/packages/pointer_interceptor) | [![pub points](https://img.shields.io/pub/points/pointer_interceptor)](https://pub.dev/packages/pointer_interceptor/score) | [![popularity](https://img.shields.io/pub/popularity/pointer_interceptor)](https://pub.dev/packages/pointer_interceptor/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20pointer_interceptor?label=)](https://github.com/flutter/flutter/labels/p%3A%20pointer_interceptor) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p:%20pointer_interceptor?label=)](https://github.com/flutter/packages/labels/p%3A%20pointer_interceptor) | | [plugin\_platform\_interface](./packages/plugin_platform_interface/) | [![pub package](https://img.shields.io/pub/v/plugin_platform_interface.svg)](https://pub.dev/packages/plugin_platform_interface) | [![pub points](https://img.shields.io/pub/points/plugin_platform_interface)](https://pub.dev/packages/plugin_platform_interface/score) | [![popularity](https://img.shields.io/pub/popularity/plugin_platform_interface)](https://pub.dev/packages/plugin_platform_interface/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20plugin_platform_interface?label=)](https://github.com/flutter/flutter/labels/p%3A%20plugin_platform_interface) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p:%20plugin_platform_interface?label=)](https://github.com/flutter/packages/labels/p%3A%20plugin_platform_interface) | | [quick\_actions](./packages/quick_actions/) | [![pub package](https://img.shields.io/pub/v/quick_actions.svg)](https://pub.dev/packages/quick_actions) | [![pub points](https://img.shields.io/pub/points/quick_actions)](https://pub.dev/packages/quick_actions/score) | [![popularity](https://img.shields.io/pub/popularity/quick_actions)](https://pub.dev/packages/quick_actions/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20quick_actions?label=)](https://github.com/flutter/flutter/labels/p%3A%20quick_actions) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p:%20quick_actions?label=)](https://github.com/flutter/packages/labels/p%3A%20quick_actions) | diff --git a/packages/platform/.gitignore b/packages/platform/.gitignore new file mode 100644 index 000000000000..dfbf1b43f037 --- /dev/null +++ b/packages/platform/.gitignore @@ -0,0 +1,17 @@ +### Dart template +# Don’t commit the following directories created by pub. +.buildlog +.dart_tool/ +.pub/ +build/ +packages +.packages + +# Include when developing application packages. +pubspec.lock + +# IDE +.project +.settings +.idea +.c9 diff --git a/packages/platform/AUTHORS b/packages/platform/AUTHORS new file mode 100644 index 000000000000..ad59f1184179 --- /dev/null +++ b/packages/platform/AUTHORS @@ -0,0 +1,6 @@ +# Below is a list of people and organizations that have contributed +# to the Process project. Names should be added to the list like so: +# +# Name/Organization + +Google Inc. diff --git a/packages/platform/CHANGELOG.md b/packages/platform/CHANGELOG.md new file mode 100644 index 000000000000..98ea97f2ce6a --- /dev/null +++ b/packages/platform/CHANGELOG.md @@ -0,0 +1,87 @@ +## 3.1.1 + +* Transfers the package source from https://github.com/google/platform.dart to + https://github.com/flutter/packages. + +## 3.1.0 + +* Removed `Platform.packageRoot`, which was already marked deprecated, and which + didn't work in Dart 2. + +## 3.0.2 + +* Added `FakePlatform.copyWith` function. + +## 3.0.1 + +* Added string constants for each of the supported platforms for use in switch + statements. + +## 3.0.0 + +* First stable null safe release. + +## 3.0.0-nullsafety.4 + +* Update supported SDK range. + +## 3.0.0-nullsafety.3 + +* Update supported SDK range. + +## 3.0.0-nullsafety.2 + +* Update supported SDK range. + +## 3.0.0-nullsafety.1 + +* Migrate package to null-safe dart. + +## 2.2.1 + +* Add `operatingSystemVersion` + +## 2.2.0 + +* Declare compatibility with Dart 2 stable +* Update dependency on `package:test` to 1.0 + +## 2.1.2 + +* Relax sdk upper bound constraint to '<2.0.0' to allow 'edge' dart sdk use. + +## 2.1.1 + +* Bumped maximum Dart SDK version to 2.0.0-dev.infinity + +## 2.1.0 + +* Added `localeName` +* Bumped minimum Dart SDK version to 1.24.0-dev.0.0 + +## 2.0.0 + +* Added `stdinSupportsAnsi` and `stdinSupportsAnsi` +* Removed `ansiSupported` + +## 1.1.1 + +* Updated `LocalPlatform` to use new `dart.io` API for ansi color support queries +* Bumped minimum Dart SDK version to 1.23.0-dev.10.0 + +## 1.1.0 + +* Added `ansiSupported` +* Bumped minimum Dart SDK version to 1.23.0-dev.9.0 + +## 1.0.2 + +* Minor doc updates + +## 1.0.1 + +* Added const constructors for `Platform` and `LocalPlatform` + +## 1.0.0 + +* Initial version diff --git a/packages/platform/LICENSE b/packages/platform/LICENSE new file mode 100644 index 000000000000..c6823b81eb84 --- /dev/null +++ b/packages/platform/LICENSE @@ -0,0 +1,25 @@ +Copyright 2013 The Flutter Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/packages/platform/README.md b/packages/platform/README.md new file mode 100644 index 000000000000..44932a672318 --- /dev/null +++ b/packages/platform/README.md @@ -0,0 +1,10 @@ +[![Pub](https://img.shields.io/pub/v/platform.svg)](https://pub.dartlang.org/packages/platform) + +A generic platform abstraction for Dart. + +Like `dart:io`, `package:platform` supplies a rich, Dart-idiomatic API for +accessing platform-specific information. + +`package:platform` provides a lightweight wrapper around the static `Platform` +properties that exist in `dart:io`. However, it uses instance properties rather +than static properties, making it possible to mock out in tests. diff --git a/packages/platform/dart_test.yaml b/packages/platform/dart_test.yaml new file mode 100644 index 000000000000..91ec220b8e22 --- /dev/null +++ b/packages/platform/dart_test.yaml @@ -0,0 +1 @@ +test_on: vm diff --git a/packages/platform/lib/platform.dart b/packages/platform/lib/platform.dart new file mode 100644 index 000000000000..91cd76e3bb46 --- /dev/null +++ b/packages/platform/lib/platform.dart @@ -0,0 +1,8 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/// Core interfaces & classes. +export 'src/interface/local_platform.dart'; +export 'src/interface/platform.dart'; +export 'src/testing/fake_platform.dart'; diff --git a/packages/platform/lib/src/interface/local_platform.dart b/packages/platform/lib/src/interface/local_platform.dart new file mode 100644 index 000000000000..c24c01be55e1 --- /dev/null +++ b/packages/platform/lib/src/interface/local_platform.dart @@ -0,0 +1,58 @@ +// Copyright 2013 The Flutter Authors. 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' as io show Platform, stdin, stdout; + +import 'platform.dart'; + +/// `Platform` implementation that delegates directly to `dart:io`. +class LocalPlatform extends Platform { + /// Creates a new [LocalPlatform]. + const LocalPlatform(); + + @override + int get numberOfProcessors => io.Platform.numberOfProcessors; + + @override + String get pathSeparator => io.Platform.pathSeparator; + + @override + String get operatingSystem => io.Platform.operatingSystem; + + @override + String get operatingSystemVersion => io.Platform.operatingSystemVersion; + + @override + String get localHostname => io.Platform.localHostname; + + @override + Map get environment => io.Platform.environment; + + @override + String get executable => io.Platform.executable; + + @override + String get resolvedExecutable => io.Platform.resolvedExecutable; + + @override + Uri get script => io.Platform.script; + + @override + List get executableArguments => io.Platform.executableArguments; + + @override + String? get packageConfig => io.Platform.packageConfig; + + @override + String get version => io.Platform.version; + + @override + bool get stdinSupportsAnsi => io.stdin.supportsAnsiEscapes; + + @override + bool get stdoutSupportsAnsi => io.stdout.supportsAnsiEscapes; + + @override + String get localeName => io.Platform.localeName; +} diff --git a/packages/platform/lib/src/interface/platform.dart b/packages/platform/lib/src/interface/platform.dart new file mode 100644 index 000000000000..dcc47be82a38 --- /dev/null +++ b/packages/platform/lib/src/interface/platform.dart @@ -0,0 +1,205 @@ +// Copyright 2013 The Flutter Authors. 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:convert'; + +/// Provides API parity with the `Platform` class in `dart:io`, but using +/// instance properties rather than static properties. This difference enables +/// the use of these APIs in tests, where you can provide mock implementations. +abstract class Platform { + /// Creates a new [Platform]. + const Platform(); + + /// A string constant to compare with [operatingSystem] to see if the platform + /// is Linux. + /// + /// Useful in case statements when switching on [operatingSystem]. + /// + /// To just check if the platform is Linux, use [isLinux]. + static const String linux = 'linux'; + + /// A string constant to compare with [operatingSystem] to see if the platform + /// is Windows. + /// + /// Useful in case statements when switching on [operatingSystem]. + /// + /// To just check if the platform is Windows, use [isWindows]. + static const String windows = 'windows'; + + /// A string constant to compare with [operatingSystem] to see if the platform + /// is macOS. + /// + /// Useful in case statements when switching on [operatingSystem]. + /// + /// To just check if the platform is macOS, use [isMacOS]. + static const String macOS = 'macos'; + + /// A string constant to compare with [operatingSystem] to see if the platform + /// is Android. + /// + /// Useful in case statements when switching on [operatingSystem]. + /// + /// To just check if the platform is Android, use [isAndroid]. + static const String android = 'android'; + + /// A string constant to compare with [operatingSystem] to see if the platform + /// is iOS. + /// + /// Useful in case statements when switching on [operatingSystem]. + /// + /// To just check if the platform is iOS, use [isIOS]. + static const String iOS = 'ios'; + + /// A string constant to compare with [operatingSystem] to see if the platform + /// is Fuchsia. + /// + /// Useful in case statements when switching on [operatingSystem]. + /// + /// To just check if the platform is Fuchsia, use [isFuchsia]. + static const String fuchsia = 'fuchsia'; + + /// A list of the possible values that [operatingSystem] can return. + static const List operatingSystemValues = [ + linux, + macOS, + windows, + android, + iOS, + fuchsia, + ]; + + /// The number of processors of the machine. + int get numberOfProcessors; + + /// The path separator used by the operating system to separate + /// components in file paths. + String get pathSeparator; + + /// A string (`linux`, `macos`, `windows`, `android`, `ios`, or `fuchsia`) + /// representing the operating system. + /// + /// The possible return values are available from [operatingSystemValues], and + /// there are constants for each of the platforms to use in switch statements + /// or conditionals (See [linux], [macOS], [windows], [android], [iOS], and + /// [fuchsia]). + String get operatingSystem; + + /// A string representing the version of the operating system or platform. + String get operatingSystemVersion; + + /// Get the local hostname for the system. + String get localHostname; + + /// True if the operating system is Linux. + bool get isLinux => operatingSystem == linux; + + /// True if the operating system is OS X. + bool get isMacOS => operatingSystem == macOS; + + /// True if the operating system is Windows. + bool get isWindows => operatingSystem == windows; + + /// True if the operating system is Android. + bool get isAndroid => operatingSystem == android; + + /// True if the operating system is iOS. + bool get isIOS => operatingSystem == iOS; + + /// True if the operating system is Fuchsia + bool get isFuchsia => operatingSystem == fuchsia; + + /// The environment for this process. + /// + /// The returned environment is an unmodifiable map whose content is + /// retrieved from the operating system on its first use. + /// + /// Environment variables on Windows are case-insensitive. The map + /// returned on Windows is therefore case-insensitive and will convert + /// all keys to upper case. On other platforms the returned map is + /// a standard case-sensitive map. + Map get environment; + + /// The path of the executable used to run the script in this isolate. + /// + /// The path returned is the literal path used to run the script. This + /// path might be relative or just be a name from which the executable + /// was found by searching the `PATH`. + /// + /// To get the absolute path to the resolved executable use + /// [resolvedExecutable]. + String get executable; + + /// The path of the executable used to run the script in this + /// isolate after it has been resolved by the OS. + /// + /// This is the absolute path, with all symlinks resolved, to the + /// executable used to run the script. + String get resolvedExecutable; + + /// The absolute URI of the script being run in this + /// isolate. + /// + /// If the script argument on the command line is relative, + /// it is resolved to an absolute URI before fetching the script, and + /// this absolute URI is returned. + /// + /// URI resolution only does string manipulation on the script path, and this + /// may be different from the file system's path resolution behavior. For + /// example, a symbolic link immediately followed by '..' will not be + /// looked up. + /// + /// If the executable environment does not support [script] an empty + /// [Uri] is returned. + Uri get script; + + /// The flags passed to the executable used to run the script in this + /// isolate. These are the command-line flags between the executable name + /// and the script name. Each fetch of `executableArguments` returns a new + /// list containing the flags passed to the executable. + List get executableArguments; + + /// The value of the `--packages` flag passed to the executable + /// used to run the script in this isolate. This is the configuration which + /// specifies how Dart packages are looked up. + /// + /// If there is no `--packages` flag, `null` is returned. + String? get packageConfig; + + /// The version of the current Dart runtime. + /// + /// The returned `String` is formatted as the [semver](http://semver.org) + /// version string of the current dart runtime, possibly followed by + /// whitespace and other version and build details. + String get version; + + /// When stdin is connected to a terminal, whether ANSI codes are supported. + bool get stdinSupportsAnsi; + + /// When stdout is connected to a terminal, whether ANSI codes are supported. + bool get stdoutSupportsAnsi; + + /// Get the name of the current locale. + String get localeName; + + /// Returns a JSON-encoded representation of this platform. + String toJson() { + return const JsonEncoder.withIndent(' ').convert({ + 'numberOfProcessors': numberOfProcessors, + 'pathSeparator': pathSeparator, + 'operatingSystem': operatingSystem, + 'operatingSystemVersion': operatingSystemVersion, + 'localHostname': localHostname, + 'environment': environment, + 'executable': executable, + 'resolvedExecutable': resolvedExecutable, + 'script': script.toString(), + 'executableArguments': executableArguments, + 'packageConfig': packageConfig, + 'version': version, + 'stdinSupportsAnsi': stdinSupportsAnsi, + 'stdoutSupportsAnsi': stdoutSupportsAnsi, + 'localeName': localeName, + }); + } +} diff --git a/packages/platform/lib/src/testing/fake_platform.dart b/packages/platform/lib/src/testing/fake_platform.dart new file mode 100644 index 000000000000..43743d6ef138 --- /dev/null +++ b/packages/platform/lib/src/testing/fake_platform.dart @@ -0,0 +1,199 @@ +// Copyright 2013 The Flutter Authors. 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:convert'; + +import '../interface/platform.dart'; + +/// Provides a mutable implementation of the [Platform] interface. +class FakePlatform extends Platform { + /// Creates a new [FakePlatform] with the specified properties. + /// + /// Unspecified properties will *not* be assigned default values (they will + /// remain `null`). If an unset non-null value is read, a [StateError] will + /// be thrown instead of returning `null`. + FakePlatform({ + int? numberOfProcessors, + String? pathSeparator, + String? operatingSystem, + String? operatingSystemVersion, + String? localHostname, + Map? environment, + String? executable, + String? resolvedExecutable, + Uri? script, + List? executableArguments, + this.packageConfig, + String? version, + bool? stdinSupportsAnsi, + bool? stdoutSupportsAnsi, + String? localeName, + }) : _numberOfProcessors = numberOfProcessors, + _pathSeparator = pathSeparator, + _operatingSystem = operatingSystem, + _operatingSystemVersion = operatingSystemVersion, + _localHostname = localHostname, + _environment = environment, + _executable = executable, + _resolvedExecutable = resolvedExecutable, + _script = script, + _executableArguments = executableArguments, + _version = version, + _stdinSupportsAnsi = stdinSupportsAnsi, + _stdoutSupportsAnsi = stdoutSupportsAnsi, + _localeName = localeName; + + /// Creates a new [FakePlatform] with properties whose initial values mirror + /// the specified [platform]. + FakePlatform.fromPlatform(Platform platform) + : _numberOfProcessors = platform.numberOfProcessors, + _pathSeparator = platform.pathSeparator, + _operatingSystem = platform.operatingSystem, + _operatingSystemVersion = platform.operatingSystemVersion, + _localHostname = platform.localHostname, + _environment = Map.from(platform.environment), + _executable = platform.executable, + _resolvedExecutable = platform.resolvedExecutable, + _script = platform.script, + _executableArguments = List.from(platform.executableArguments), + packageConfig = platform.packageConfig, + _version = platform.version, + _stdinSupportsAnsi = platform.stdinSupportsAnsi, + _stdoutSupportsAnsi = platform.stdoutSupportsAnsi, + _localeName = platform.localeName; + + /// Creates a new [FakePlatform] with properties extracted from the encoded + /// JSON string. + /// + /// [json] must be a JSON string that matches the encoding produced by + /// [toJson]. + factory FakePlatform.fromJson(String json) { + final Map map = + const JsonDecoder().convert(json) as Map; + return FakePlatform( + numberOfProcessors: map['numberOfProcessors'] as int?, + pathSeparator: map['pathSeparator'] as String?, + operatingSystem: map['operatingSystem'] as String?, + operatingSystemVersion: map['operatingSystemVersion'] as String?, + localHostname: map['localHostname'] as String?, + environment: + (map['environment'] as Map).cast(), + executable: map['executable'] as String?, + resolvedExecutable: map['resolvedExecutable'] as String?, + script: Uri.parse(map['script'] as String), + executableArguments: + (map['executableArguments'] as List).cast(), + packageConfig: map['packageConfig'] as String?, + version: map['version'] as String?, + stdinSupportsAnsi: map['stdinSupportsAnsi'] as bool?, + stdoutSupportsAnsi: map['stdoutSupportsAnsi'] as bool?, + localeName: map['localeName'] as String?, + ); + } + + /// Creates a new [FakePlatform] from this one, with some properties replaced by the given properties. + FakePlatform copyWith({ + int? numberOfProcessors, + String? pathSeparator, + String? operatingSystem, + String? operatingSystemVersion, + String? localHostname, + Map? environment, + String? executable, + String? resolvedExecutable, + Uri? script, + List? executableArguments, + String? packageConfig, + String? version, + bool? stdinSupportsAnsi, + bool? stdoutSupportsAnsi, + String? localeName, + }) { + return FakePlatform( + numberOfProcessors: numberOfProcessors ?? this.numberOfProcessors, + pathSeparator: pathSeparator ?? this.pathSeparator, + operatingSystem: operatingSystem ?? this.operatingSystem, + operatingSystemVersion: + operatingSystemVersion ?? this.operatingSystemVersion, + localHostname: localHostname ?? this.localHostname, + environment: environment ?? this.environment, + executable: executable ?? this.executable, + resolvedExecutable: resolvedExecutable ?? this.resolvedExecutable, + script: script ?? this.script, + executableArguments: executableArguments ?? this.executableArguments, + packageConfig: packageConfig ?? this.packageConfig, + version: version ?? this.version, + stdinSupportsAnsi: stdinSupportsAnsi ?? this.stdinSupportsAnsi, + stdoutSupportsAnsi: stdoutSupportsAnsi ?? this.stdoutSupportsAnsi, + localeName: localeName ?? this.localeName, + ); + } + + @override + int get numberOfProcessors => _throwIfNull(_numberOfProcessors); + int? _numberOfProcessors; + + @override + String get pathSeparator => _throwIfNull(_pathSeparator); + String? _pathSeparator; + + @override + String get operatingSystem => _throwIfNull(_operatingSystem); + String? _operatingSystem; + + @override + String get operatingSystemVersion => _throwIfNull(_operatingSystemVersion); + String? _operatingSystemVersion; + + @override + String get localHostname => _throwIfNull(_localHostname); + String? _localHostname; + + @override + Map get environment => _throwIfNull(_environment); + Map? _environment; + + @override + String get executable => _throwIfNull(_executable); + String? _executable; + + @override + String get resolvedExecutable => _throwIfNull(_resolvedExecutable); + String? _resolvedExecutable; + + @override + Uri get script => _throwIfNull(_script); + Uri? _script; + + @override + List get executableArguments => _throwIfNull(_executableArguments); + List? _executableArguments; + + @override + String? packageConfig; + + @override + String get version => _throwIfNull(_version); + String? _version; + + @override + bool get stdinSupportsAnsi => _throwIfNull(_stdinSupportsAnsi); + bool? _stdinSupportsAnsi; + + @override + bool get stdoutSupportsAnsi => _throwIfNull(_stdoutSupportsAnsi); + bool? _stdoutSupportsAnsi; + + @override + String get localeName => _throwIfNull(_localeName); + String? _localeName; + + T _throwIfNull(T? value) { + if (value == null) { + throw StateError( + 'Tried to read property of FakePlatform but it was unset.'); + } + return value; + } +} diff --git a/packages/platform/pubspec.yaml b/packages/platform/pubspec.yaml new file mode 100644 index 000000000000..fadcae889fb4 --- /dev/null +++ b/packages/platform/pubspec.yaml @@ -0,0 +1,11 @@ +name: platform +description: A pluggable, mockable platform information abstraction for Dart. +repository: https://github.com/flutter/packages/tree/main/packages/platform +issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+platform%22 +version: 3.1.1 + +environment: + sdk: ">=2.18.0 <3.0.0" + +dev_dependencies: + test: ^1.16.8 diff --git a/packages/platform/test/fake_platform_test.dart b/packages/platform/test/fake_platform_test.dart new file mode 100644 index 000000000000..08795ce98524 --- /dev/null +++ b/packages/platform/test/fake_platform_test.dart @@ -0,0 +1,165 @@ +// Copyright 2013 The Flutter Authors. 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' as io; + +import 'package:platform/platform.dart'; +import 'package:test/test.dart'; + +void _expectPlatformsEqual(Platform actual, Platform expected) { + expect(actual.numberOfProcessors, expected.numberOfProcessors); + expect(actual.pathSeparator, expected.pathSeparator); + expect(actual.operatingSystem, expected.operatingSystem); + expect(actual.operatingSystemVersion, expected.operatingSystemVersion); + expect(actual.localHostname, expected.localHostname); + expect(actual.environment, expected.environment); + expect(actual.executable, expected.executable); + expect(actual.resolvedExecutable, expected.resolvedExecutable); + expect(actual.script, expected.script); + expect(actual.executableArguments, expected.executableArguments); + expect(actual.packageConfig, expected.packageConfig); + expect(actual.version, expected.version); + expect(actual.localeName, expected.localeName); +} + +void main() { + group('FakePlatform', () { + late FakePlatform fake; + late LocalPlatform local; + + setUp(() { + fake = FakePlatform(); + local = const LocalPlatform(); + }); + + group('fromPlatform', () { + setUp(() { + fake = FakePlatform.fromPlatform(local); + }); + + test('copiesAllProperties', () { + _expectPlatformsEqual(fake, local); + }); + + test('convertsPropertiesToMutable', () { + final String key = fake.environment.keys.first; + + expect(fake.environment[key], local.environment[key]); + fake.environment[key] = 'FAKE'; + expect(fake.environment[key], 'FAKE'); + + expect( + fake.executableArguments.length, local.executableArguments.length); + fake.executableArguments.add('ARG'); + expect(fake.executableArguments.last, 'ARG'); + }); + }); + + group('copyWith', () { + setUp(() { + fake = FakePlatform.fromPlatform(local); + }); + + test('overrides a value, but leaves others intact', () { + final FakePlatform copy = fake.copyWith( + numberOfProcessors: -1, + ); + expect(copy.numberOfProcessors, equals(-1)); + expect(copy.pathSeparator, local.pathSeparator); + expect(copy.operatingSystem, local.operatingSystem); + expect(copy.operatingSystemVersion, local.operatingSystemVersion); + expect(copy.localHostname, local.localHostname); + expect(copy.environment, local.environment); + expect(copy.executable, local.executable); + expect(copy.resolvedExecutable, local.resolvedExecutable); + expect(copy.script, local.script); + expect(copy.executableArguments, local.executableArguments); + expect(copy.packageConfig, local.packageConfig); + expect(copy.version, local.version); + expect(copy.localeName, local.localeName); + }); + test('can override all values', () { + fake = FakePlatform( + numberOfProcessors: 8, + pathSeparator: ':', + operatingSystem: 'fake', + operatingSystemVersion: '0.1.0', + localHostname: 'host', + environment: {'PATH': '.'}, + executable: 'executable', + resolvedExecutable: '/executable', + script: Uri.file('/platform/test/fake_platform_test.dart'), + executableArguments: ['scriptarg'], + version: '0.1.1', + stdinSupportsAnsi: false, + stdoutSupportsAnsi: true, + localeName: 'local', + ); + final FakePlatform copy = fake.copyWith( + numberOfProcessors: local.numberOfProcessors, + pathSeparator: local.pathSeparator, + operatingSystem: local.operatingSystem, + operatingSystemVersion: local.operatingSystemVersion, + localHostname: local.localHostname, + environment: local.environment, + executable: local.executable, + resolvedExecutable: local.resolvedExecutable, + script: local.script, + executableArguments: local.executableArguments, + packageConfig: local.packageConfig, + version: local.version, + stdinSupportsAnsi: local.stdinSupportsAnsi, + stdoutSupportsAnsi: local.stdoutSupportsAnsi, + localeName: local.localeName, + ); + _expectPlatformsEqual(copy, local); + }); + }); + + group('json', () { + test('fromJson', () { + final String json = io.File('test/platform.json').readAsStringSync(); + fake = FakePlatform.fromJson(json); + expect(fake.numberOfProcessors, 8); + expect(fake.pathSeparator, '/'); + expect(fake.operatingSystem, 'macos'); + expect(fake.operatingSystemVersion, '10.14.5'); + expect(fake.localHostname, 'platform.test.org'); + expect(fake.environment, { + 'PATH': '/bin', + 'PWD': '/platform', + }); + expect(fake.executable, '/bin/dart'); + expect(fake.resolvedExecutable, '/bin/dart'); + expect(fake.script, Uri.file('/platform/test/fake_platform_test.dart')); + expect(fake.executableArguments, ['--checked']); + expect(fake.packageConfig, null); + expect(fake.version, '1.22.0'); + expect(fake.localeName, 'de/de'); + }); + + test('fromJsonToJson', () { + fake = FakePlatform.fromJson(local.toJson()); + _expectPlatformsEqual(fake, local); + }); + }); + }); + + test('Throws when unset non-null values are read', () { + final FakePlatform platform = FakePlatform(); + + expect(() => platform.numberOfProcessors, throwsA(isStateError)); + expect(() => platform.pathSeparator, throwsA(isStateError)); + expect(() => platform.operatingSystem, throwsA(isStateError)); + expect(() => platform.operatingSystemVersion, throwsA(isStateError)); + expect(() => platform.localHostname, throwsA(isStateError)); + expect(() => platform.environment, throwsA(isStateError)); + expect(() => platform.executable, throwsA(isStateError)); + expect(() => platform.resolvedExecutable, throwsA(isStateError)); + expect(() => platform.script, throwsA(isStateError)); + expect(() => platform.executableArguments, throwsA(isStateError)); + expect(() => platform.version, throwsA(isStateError)); + expect(() => platform.localeName, throwsA(isStateError)); + }); +} diff --git a/packages/platform/test/platform.json b/packages/platform/test/platform.json new file mode 100644 index 000000000000..60b7c139d8ac --- /dev/null +++ b/packages/platform/test/platform.json @@ -0,0 +1,20 @@ +{ + "numberOfProcessors": 8, + "pathSeparator": "/", + "operatingSystem": "macos", + "operatingSystemVersion": "10.14.5", + "localHostname": "platform.test.org", + "environment": { + "PATH": "/bin", + "PWD": "/platform" + }, + "executable": "/bin/dart", + "resolvedExecutable": "/bin/dart", + "script": "file:///platform/test/fake_platform_test.dart", + "executableArguments": [ + "--checked" + ], + "packageConfig": null, + "version": "1.22.0", + "localeName": "de/de" +} \ No newline at end of file