From ae5560596c87623cc923a081247097a6d2e1cc89 Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Fri, 19 Feb 2021 19:58:05 -0500 Subject: [PATCH 1/3] Update to stable --- .../path_provider/path_provider/CHANGELOG.md | 7 +------ .../path_provider/example/pubspec.yaml | 4 ++-- .../path_provider/path_provider/pubspec.yaml | 18 +++++++++--------- 3 files changed, 12 insertions(+), 17 deletions(-) diff --git a/packages/path_provider/path_provider/CHANGELOG.md b/packages/path_provider/path_provider/CHANGELOG.md index a52711bf0736..fd3022c9c7ab 100644 --- a/packages/path_provider/path_provider/CHANGELOG.md +++ b/packages/path_provider/path_provider/CHANGELOG.md @@ -1,9 +1,4 @@ -## 2.0.0-nullsafety.1 - -* Require latest path_provider_windows to avoid potential issues - with breaking changes in `ffi` and `win32`. - -## 2.0.0-nullsafety +## 2.0.0 * Migrate to null safety. diff --git a/packages/path_provider/path_provider/example/pubspec.yaml b/packages/path_provider/path_provider/example/pubspec.yaml index cef0449ca01a..68c751a81843 100644 --- a/packages/path_provider/path_provider/example/pubspec.yaml +++ b/packages/path_provider/path_provider/example/pubspec.yaml @@ -17,11 +17,11 @@ dev_dependencies: path: ../../../integration_test flutter_driver: sdk: flutter - pedantic: ^1.8.0 + pedantic: ^1.10.0 flutter: uses-material-design: true environment: - sdk: ">=2.12.0-0 <3.0.0" + sdk: ">=2.12.0-259.9.beta <3.0.0" flutter: ">=1.12.13+hotfix.5" diff --git a/packages/path_provider/path_provider/pubspec.yaml b/packages/path_provider/path_provider/pubspec.yaml index 3d79c99e2223..e14fc6f3cd17 100644 --- a/packages/path_provider/path_provider/pubspec.yaml +++ b/packages/path_provider/path_provider/pubspec.yaml @@ -1,7 +1,7 @@ name: path_provider description: Flutter plugin for getting commonly used locations on host platform file systems, such as the temp and app data directories. homepage: https://github.com/flutter/plugins/tree/master/packages/path_provider/path_provider -version: 2.0.0-nullsafety.1 +version: 2.0.0 flutter: plugin: @@ -21,10 +21,10 @@ flutter: dependencies: flutter: sdk: flutter - path_provider_platform_interface: ^2.0.0-nullsafety - path_provider_macos: ^0.0.5-nullsafety - path_provider_linux: ^0.2.0-nullsafety - path_provider_windows: ^0.1.0-nullsafety.3 + path_provider_platform_interface: ^2.0.0 + path_provider_macos: ^2.0.0 + path_provider_linux: ^2.0.0 + path_provider_windows: ^2.0.0 dev_dependencies: integration_test: @@ -33,10 +33,10 @@ dev_dependencies: sdk: flutter flutter_driver: sdk: flutter - pedantic: ^1.10.0-nullsafety - mockito: ^5.0.0-nullsafety.0 - plugin_platform_interface: ^1.1.0-nullsafety + pedantic: ^1.10.0 + mockito: ^5.0.0-nullsafety.7 + plugin_platform_interface: ">=1.0.0 <3.0.0" environment: - sdk: ">=2.12.0-0 <3.0.0" + sdk: ">=2.12.0-259.9.beta <3.0.0" flutter: ">=1.12.13+hotfix.5" From c11a360b7dc6a14ccd48ff96603ee8ac64339ec0 Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Fri, 19 Feb 2021 20:37:50 -0500 Subject: [PATCH 2/3] Make several of the methods non-nullable --- .../path_provider/path_provider/CHANGELOG.md | 4 + .../path_provider/lib/path_provider.dart | 40 ++++-- .../path_provider/path_provider/pubspec.yaml | 2 +- .../test/path_provider_test.dart | 127 +++++++++++++++--- 4 files changed, 142 insertions(+), 31 deletions(-) diff --git a/packages/path_provider/path_provider/CHANGELOG.md b/packages/path_provider/path_provider/CHANGELOG.md index fd3022c9c7ab..c28c617bbea4 100644 --- a/packages/path_provider/path_provider/CHANGELOG.md +++ b/packages/path_provider/path_provider/CHANGELOG.md @@ -1,6 +1,10 @@ ## 2.0.0 * Migrate to null safety. +* BREAKING CHANGE: Path accessors that return non-nullable results will throw + a `MissingPlatformDirectoryException` if the platform implementation is unable + to get the corresponding directory (except on platforms where the method is + explicitly unsupported, where they will continue to throw `UnsupportedError`). ## 1.6.28 diff --git a/packages/path_provider/path_provider/lib/path_provider.dart b/packages/path_provider/path_provider/lib/path_provider.dart index 1560c3399e72..06cda2e4ccd7 100644 --- a/packages/path_provider/path_provider/lib/path_provider.dart +++ b/packages/path_provider/path_provider/lib/path_provider.dart @@ -20,6 +20,27 @@ set disablePathProviderPlatformOverride(bool override) {} bool _manualDartRegistrationNeeded = true; +/// An exception thrown when a directory that should always be available on +/// the current platform cannot be obtained. +class MissingPlatformDirectoryException implements Exception { + /// Creates a new exception + MissingPlatformDirectoryException(this.message, {this.details}); + + /// The explanation of the exception. + final String message; + + /// Added details, if any. + /// + /// E.g., an error object from the platform implementation. + final Object? details; + + @override + String toString() { + String detailsAddition = details == null ? '' : ': $details'; + return 'MissingPlatformDirectoryException($message)$detailsAddition'; + } +} + PathProviderPlatform get _platform { // This is to manually endorse Dart implementations until automatic // registration of Dart plugins is implemented. For details see @@ -51,10 +72,11 @@ PathProviderPlatform get _platform { /// On iOS, this uses the `NSCachesDirectory` API. /// /// On Android, this uses the `getCacheDir` API on the context. -Future getTemporaryDirectory() async { +Future getTemporaryDirectory() async { final String? path = await _platform.getTemporaryPath(); if (path == null) { - return null; + throw MissingPlatformDirectoryException( + 'Unable to get temporary directory'); } return Directory(path); } @@ -69,10 +91,11 @@ Future getTemporaryDirectory() async { /// If this directory does not exist, it is created automatically. /// /// On Android, this function uses the `getFilesDir` API on the context. -Future getApplicationSupportDirectory() async { +Future getApplicationSupportDirectory() async { final String? path = await _platform.getApplicationSupportPath(); if (path == null) { - return null; + throw MissingPlatformDirectoryException( + 'Unable to get application support directory'); } return Directory(path); @@ -83,10 +106,10 @@ Future getApplicationSupportDirectory() async { /// /// On Android, this function throws an [UnsupportedError] as no equivalent /// path exists. -Future getLibraryDirectory() async { +Future getLibraryDirectory() async { final String? path = await _platform.getLibraryPath(); if (path == null) { - return null; + throw MissingPlatformDirectoryException('Unable to get library directory'); } return Directory(path); } @@ -100,10 +123,11 @@ Future getLibraryDirectory() async { /// On Android, this uses the `getDataDirectory` API on the context. Consider /// using [getExternalStorageDirectory] instead if data is intended to be visible /// to the user. -Future getApplicationDocumentsDirectory() async { +Future getApplicationDocumentsDirectory() async { final String? path = await _platform.getApplicationDocumentsPath(); if (path == null) { - return null; + throw MissingPlatformDirectoryException( + 'Unable to get application documents directory'); } return Directory(path); } diff --git a/packages/path_provider/path_provider/pubspec.yaml b/packages/path_provider/path_provider/pubspec.yaml index e14fc6f3cd17..81941dac67b1 100644 --- a/packages/path_provider/path_provider/pubspec.yaml +++ b/packages/path_provider/path_provider/pubspec.yaml @@ -34,8 +34,8 @@ dev_dependencies: flutter_driver: sdk: flutter pedantic: ^1.10.0 - mockito: ^5.0.0-nullsafety.7 plugin_platform_interface: ">=1.0.0 <3.0.0" + test: ^1.16.0 environment: sdk: ">=2.12.0-259.9.beta <3.0.0" diff --git a/packages/path_provider/path_provider/test/path_provider_test.dart b/packages/path_provider/path_provider/test/path_provider_test.dart index aec5e060f631..759e9c09ffc2 100644 --- a/packages/path_provider/path_provider/test/path_provider_test.dart +++ b/packages/path_provider/path_provider/test/path_provider_test.dart @@ -6,10 +6,10 @@ import 'dart:io' show Directory; import 'dart:async'; import 'package:flutter_test/flutter_test.dart'; -import 'package:mockito/mockito.dart'; import 'package:path_provider/path_provider.dart'; import 'package:path_provider_platform_interface/path_provider_platform_interface.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; +import 'package:test/fake.dart'; const String kTemporaryPath = 'temporaryPath'; const String kApplicationSupportPath = 'applicationSupportPath'; @@ -20,31 +20,30 @@ const String kExternalCachePath = 'externalCachePath'; const String kExternalStoragePath = 'externalStoragePath'; void main() { - group('PathProvider', () { - TestWidgetsFlutterBinding.ensureInitialized(); - + TestWidgetsFlutterBinding.ensureInitialized(); + group('PathProvider full implementation', () { setUp(() async { - PathProviderPlatform.instance = MockPathProviderPlatform(); + PathProviderPlatform.instance = FakePathProviderPlatform(); }); test('getTemporaryDirectory', () async { - Directory? result = await getTemporaryDirectory(); - expect(result?.path, kTemporaryPath); + Directory result = await getTemporaryDirectory(); + expect(result.path, kTemporaryPath); }); test('getApplicationSupportDirectory', () async { - Directory? result = await getApplicationSupportDirectory(); - expect(result?.path, kApplicationSupportPath); + Directory result = await getApplicationSupportDirectory(); + expect(result.path, kApplicationSupportPath); }); test('getLibraryDirectory', () async { - Directory? result = await getLibraryDirectory(); - expect(result?.path, kLibraryPath); + Directory result = await getLibraryDirectory(); + expect(result.path, kLibraryPath); }); test('getApplicationDocumentsDirectory', () async { - Directory? result = await getApplicationDocumentsDirectory(); - expect(result?.path, kApplicationDocumentsPath); + Directory result = await getApplicationDocumentsDirectory(); + expect(result.path, kApplicationDocumentsPath); }); test('getExternalStorageDirectory', () async { @@ -69,42 +68,126 @@ void main() { expect(result?.path, kDownloadsPath); }); }); + + group('PathProvider null implementation', () { + setUp(() async { + PathProviderPlatform.instance = AllNullFakePathProviderPlatform(); + }); + + test('getTemporaryDirectory throws on null', () async { + expect(getTemporaryDirectory(), + throwsA(isA())); + }); + + test('getApplicationSupportDirectory throws on null', () async { + expect(getApplicationSupportDirectory(), + throwsA(isA())); + }); + + test('getLibraryDirectory throws on null', () async { + expect(getLibraryDirectory(), + throwsA(isA())); + }); + + test('getApplicationDocumentsDirectory throws on null', () async { + expect(getApplicationDocumentsDirectory(), + throwsA(isA())); + }); + + test('getExternalStorageDirectory passes null through', () async { + Directory? result = await getExternalStorageDirectory(); + expect(result, isNull); + }); + + test('getExternalCacheDirectories passes null through', () async { + List? result = await getExternalCacheDirectories(); + expect(result, isNull); + }); + + test('getExternalStorageDirectories passes null through', () async { + List? result = await getExternalStorageDirectories(); + expect(result, isNull); + }); + + test('getDownloadsDirectory passses null through', () async { + Directory? result = await getDownloadsDirectory(); + expect(result, isNull); + }); + }); } -class MockPathProviderPlatform extends Mock +class FakePathProviderPlatform extends Fake with MockPlatformInterfaceMixin implements PathProviderPlatform { - Future getTemporaryPath() async { + Future getTemporaryPath() async { return kTemporaryPath; } - Future getApplicationSupportPath() async { + Future getApplicationSupportPath() async { return kApplicationSupportPath; } - Future getLibraryPath() async { + Future getLibraryPath() async { return kLibraryPath; } - Future getApplicationDocumentsPath() async { + Future getApplicationDocumentsPath() async { return kApplicationDocumentsPath; } - Future getExternalStoragePath() async { + Future getExternalStoragePath() async { return kExternalStoragePath; } - Future> getExternalCachePaths() async { + Future?> getExternalCachePaths() async { return [kExternalCachePath]; } - Future> getExternalStoragePaths({ + Future?> getExternalStoragePaths({ StorageDirectory? type, }) async { return [kExternalStoragePath]; } - Future getDownloadsPath() async { + Future getDownloadsPath() async { return kDownloadsPath; } } + +class AllNullFakePathProviderPlatform extends Fake + with MockPlatformInterfaceMixin + implements PathProviderPlatform { + Future getTemporaryPath() async { + return null; + } + + Future getApplicationSupportPath() async { + return null; + } + + Future getLibraryPath() async { + return null; + } + + Future getApplicationDocumentsPath() async { + return null; + } + + Future getExternalStoragePath() async { + return null; + } + + Future?> getExternalCachePaths() async { + return null; + } + + Future?> getExternalStoragePaths({ + StorageDirectory? type, + }) async { + return null; + } + + Future getDownloadsPath() async { + return null; + } +} From 6a646350b7628f5665b927217d097ebd44b70243 Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Fri, 19 Feb 2021 20:39:52 -0500 Subject: [PATCH 3/3] Add docs --- .../path_provider/lib/path_provider.dart | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/packages/path_provider/path_provider/lib/path_provider.dart b/packages/path_provider/path_provider/lib/path_provider.dart index 06cda2e4ccd7..da9c0b3d48a3 100644 --- a/packages/path_provider/path_provider/lib/path_provider.dart +++ b/packages/path_provider/path_provider/lib/path_provider.dart @@ -72,6 +72,9 @@ PathProviderPlatform get _platform { /// On iOS, this uses the `NSCachesDirectory` API. /// /// On Android, this uses the `getCacheDir` API on the context. +/// +/// Throws a `MissingPlatformDirectoryException` if the system is unable to +/// provide the directory. Future getTemporaryDirectory() async { final String? path = await _platform.getTemporaryPath(); if (path == null) { @@ -91,6 +94,9 @@ Future getTemporaryDirectory() async { /// If this directory does not exist, it is created automatically. /// /// On Android, this function uses the `getFilesDir` API on the context. +/// +/// Throws a `MissingPlatformDirectoryException` if the system is unable to +/// provide the directory. Future getApplicationSupportDirectory() async { final String? path = await _platform.getApplicationSupportPath(); if (path == null) { @@ -106,6 +112,9 @@ Future getApplicationSupportDirectory() async { /// /// On Android, this function throws an [UnsupportedError] as no equivalent /// path exists. +/// +/// Throws a `MissingPlatformDirectoryException` if the system is unable to +/// provide the directory on a supported platform. Future getLibraryDirectory() async { final String? path = await _platform.getLibraryPath(); if (path == null) { @@ -123,6 +132,9 @@ Future getLibraryDirectory() async { /// On Android, this uses the `getDataDirectory` API on the context. Consider /// using [getExternalStorageDirectory] instead if data is intended to be visible /// to the user. +/// +/// Throws a `MissingPlatformDirectoryException` if the system is unable to +/// provide the directory. Future getApplicationDocumentsDirectory() async { final String? path = await _platform.getApplicationDocumentsPath(); if (path == null) {