diff --git a/packages/path_provider/path_provider_foundation/CHANGELOG.md b/packages/path_provider/path_provider_foundation/CHANGELOG.md
index 69433e8b306..8fe69648209 100644
--- a/packages/path_provider/path_provider_foundation/CHANGELOG.md
+++ b/packages/path_provider/path_provider_foundation/CHANGELOG.md
@@ -1,5 +1,7 @@
-## NEXT
+## 2.5.0
+* Replaces Flutter-plugin-based implementation with direct FFI calls to
+ Foundation.
* Updates minimum supported SDK version to Flutter 3.29/Dart 3.7.
## 2.4.2
diff --git a/packages/path_provider/path_provider_foundation/CONTRIBUTING.md b/packages/path_provider/path_provider_foundation/CONTRIBUTING.md
new file mode 100644
index 00000000000..4573f44e694
--- /dev/null
+++ b/packages/path_provider/path_provider_foundation/CONTRIBUTING.md
@@ -0,0 +1,18 @@
+# Contributing
+
+## `ffigen`
+
+This package uses [ffigen](https://pub.dev/packages/ffigen) to call Foundation
+methods, rather than using the standard Flutter plugin structure. To add new
+functionality to the FFI interface, update `ffigen_config.yaml`, then run:
+
+```bash
+dart run ffigen --config ffigen_config.yaml
+```
+
+### Configuration philosophy
+
+This package intentionally uses very strict filtering rules to include only the
+necessary methods and functions. This is partially to keep the package small,
+but mostly to avoid unnecessarily generating anything that requires native code
+helpers, which would require setting up a native compilation step.
diff --git a/packages/path_provider/path_provider_foundation/darwin/RunnerTests/RunnerTests.swift b/packages/path_provider/path_provider_foundation/darwin/RunnerTests/RunnerTests.swift
deleted file mode 100644
index 67ee981673a..00000000000
--- a/packages/path_provider/path_provider_foundation/darwin/RunnerTests/RunnerTests.swift
+++ /dev/null
@@ -1,90 +0,0 @@
-// 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 XCTest
-
-@testable import path_provider_foundation
-
-#if os(iOS)
- import Flutter
-#elseif os(macOS)
- import FlutterMacOS
-#endif
-
-class RunnerTests: XCTestCase {
- func testGetTemporaryDirectory() throws {
- let plugin = PathProviderPlugin()
- let path = plugin.getDirectoryPath(type: .temp)
- XCTAssertEqual(
- path,
- NSSearchPathForDirectoriesInDomains(
- FileManager.SearchPathDirectory.cachesDirectory,
- FileManager.SearchPathDomainMask.userDomainMask,
- true
- ).first)
- }
-
- func testGetApplicationDocumentsDirectory() throws {
- let plugin = PathProviderPlugin()
- let path = plugin.getDirectoryPath(type: .applicationDocuments)
- XCTAssertEqual(
- path,
- NSSearchPathForDirectoriesInDomains(
- FileManager.SearchPathDirectory.documentDirectory,
- FileManager.SearchPathDomainMask.userDomainMask,
- true
- ).first)
- }
-
- func testGetApplicationSupportDirectory() throws {
- let plugin = PathProviderPlugin()
- let path = plugin.getDirectoryPath(type: .applicationSupport)
- #if os(iOS)
- // On iOS, the application support directory path should be just the system application
- // support path.
- XCTAssertEqual(
- path,
- NSSearchPathForDirectoriesInDomains(
- FileManager.SearchPathDirectory.applicationSupportDirectory,
- FileManager.SearchPathDomainMask.userDomainMask,
- true
- ).first)
- #else
- // On macOS, the application support directory path should be the system application
- // support path with an added subdirectory based on the app name.
- XCTAssert(
- path!.hasPrefix(
- NSSearchPathForDirectoriesInDomains(
- FileManager.SearchPathDirectory.applicationSupportDirectory,
- FileManager.SearchPathDomainMask.userDomainMask,
- true
- ).first!))
- XCTAssert(path!.hasSuffix("Example"))
- #endif
- }
-
- func testGetLibraryDirectory() throws {
- let plugin = PathProviderPlugin()
- let path = plugin.getDirectoryPath(type: .library)
- XCTAssertEqual(
- path,
- NSSearchPathForDirectoriesInDomains(
- FileManager.SearchPathDirectory.libraryDirectory,
- FileManager.SearchPathDomainMask.userDomainMask,
- true
- ).first)
- }
-
- func testGetDownloadsDirectory() throws {
- let plugin = PathProviderPlugin()
- let path = plugin.getDirectoryPath(type: .downloads)
- XCTAssertEqual(
- path,
- NSSearchPathForDirectoriesInDomains(
- FileManager.SearchPathDirectory.downloadsDirectory,
- FileManager.SearchPathDomainMask.userDomainMask,
- true
- ).first)
- }
-}
diff --git a/packages/path_provider/path_provider_foundation/darwin/path_provider_foundation.podspec b/packages/path_provider/path_provider_foundation/darwin/path_provider_foundation.podspec
deleted file mode 100644
index 8634ab149dc..00000000000
--- a/packages/path_provider/path_provider_foundation/darwin/path_provider_foundation.podspec
+++ /dev/null
@@ -1,26 +0,0 @@
-#
-# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html
-#
-Pod::Spec.new do |s|
- s.name = 'path_provider_foundation'
- s.version = '0.0.1'
- s.summary = 'An iOS and macOS implementation of the path_provider plugin.'
- s.description = <<-DESC
- An iOS and macOS implementation of the Flutter plugin for getting commonly used locations on the filesystem.
- DESC
- s.homepage = 'https://github.com/flutter/packages/tree/main/packages/path_provider/path_provider_foundation'
- s.license = { :type => 'BSD', :file => '../LICENSE' }
- s.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' }
- s.source = { :http => 'https://github.com/flutter/packages/tree/main/packages/path_provider/path_provider_foundation' }
- s.source_files = 'path_provider_foundation/Sources/path_provider_foundation/**/*.swift'
- s.ios.dependency 'Flutter'
- s.osx.dependency 'FlutterMacOS'
- s.ios.deployment_target = '12.0'
- s.osx.deployment_target = '10.14'
- s.ios.xcconfig = {
- 'LIBRARY_SEARCH_PATHS' => '$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)/ $(SDKROOT)/usr/lib/swift',
- 'LD_RUNPATH_SEARCH_PATHS' => '/usr/lib/swift',
- }
- s.swift_version = '5.0'
- s.resource_bundles = {'path_provider_foundation_privacy' => ['path_provider_foundation/Sources/path_provider_foundation/Resources/PrivacyInfo.xcprivacy']}
-end
diff --git a/packages/path_provider/path_provider_foundation/darwin/path_provider_foundation/Package.swift b/packages/path_provider/path_provider_foundation/darwin/path_provider_foundation/Package.swift
deleted file mode 100644
index e0e82689ac8..00000000000
--- a/packages/path_provider/path_provider_foundation/darwin/path_provider_foundation/Package.swift
+++ /dev/null
@@ -1,28 +0,0 @@
-// swift-tools-version: 5.9
-
-// 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 PackageDescription
-
-let package = Package(
- name: "path_provider_foundation",
- platforms: [
- .iOS("12.0"),
- .macOS("10.14"),
- ],
- products: [
- .library(name: "path-provider-foundation", targets: ["path_provider_foundation"])
- ],
- dependencies: [],
- targets: [
- .target(
- name: "path_provider_foundation",
- dependencies: [],
- resources: [
- .process("Resources")
- ]
- )
- ]
-)
diff --git a/packages/path_provider/path_provider_foundation/darwin/path_provider_foundation/Sources/path_provider_foundation/PathProviderPlugin.swift b/packages/path_provider/path_provider_foundation/darwin/path_provider_foundation/Sources/path_provider_foundation/PathProviderPlugin.swift
deleted file mode 100644
index 16f2edb84c9..00000000000
--- a/packages/path_provider/path_provider_foundation/darwin/path_provider_foundation/Sources/path_provider_foundation/PathProviderPlugin.swift
+++ /dev/null
@@ -1,75 +0,0 @@
-// 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 Foundation
-
-#if os(iOS)
- import Flutter
-#elseif os(macOS)
- import FlutterMacOS
-#endif
-
-public class PathProviderPlugin: NSObject, FlutterPlugin, PathProviderApi {
- public static func register(with registrar: FlutterPluginRegistrar) {
- let instance = PathProviderPlugin()
- // Workaround for https://github.com/flutter/flutter/issues/118103.
- #if os(iOS)
- let messenger = registrar.messenger()
- #else
- let messenger = registrar.messenger
- #endif
- PathProviderApiSetup.setUp(binaryMessenger: messenger, api: instance)
- }
-
- func getDirectoryPath(type: DirectoryType) -> String? {
- var path = getDirectory(ofType: fileManagerDirectoryForType(type))
- #if os(macOS)
- // In a non-sandboxed app, these are shared directories where applications are
- // expected to use its bundle ID as a subdirectory. (For non-sandboxed apps,
- // adding the extra path is harmless).
- // This is not done for iOS, for compatibility with older versions of the
- // plugin.
- if type == .applicationSupport || type == .applicationCache {
- if let basePath = path {
- let basePathURL = URL.init(fileURLWithPath: basePath)
- path = basePathURL.appendingPathComponent(Bundle.main.bundleIdentifier!).path
- }
- }
- #endif
- return path
- }
-
- // Returns the path for the container of the specified app group.
- func getContainerPath(appGroupIdentifier: String) -> String? {
- return FileManager.default.containerURL(
- forSecurityApplicationGroupIdentifier: appGroupIdentifier)?.path
- }
-}
-
-/// Returns the FileManager constant corresponding to the given type.
-private func fileManagerDirectoryForType(_ type: DirectoryType) -> FileManager.SearchPathDirectory {
- switch type {
- case .applicationCache:
- return FileManager.SearchPathDirectory.cachesDirectory
- case .applicationDocuments:
- return FileManager.SearchPathDirectory.documentDirectory
- case .applicationSupport:
- return FileManager.SearchPathDirectory.applicationSupportDirectory
- case .downloads:
- return FileManager.SearchPathDirectory.downloadsDirectory
- case .library:
- return FileManager.SearchPathDirectory.libraryDirectory
- case .temp:
- return FileManager.SearchPathDirectory.cachesDirectory
- }
-}
-
-/// Returns the user-domain directory of the given type.
-private func getDirectory(ofType directory: FileManager.SearchPathDirectory) -> String? {
- let paths = NSSearchPathForDirectoriesInDomains(
- directory,
- FileManager.SearchPathDomainMask.userDomainMask,
- true)
- return paths.first
-}
diff --git a/packages/path_provider/path_provider_foundation/darwin/path_provider_foundation/Sources/path_provider_foundation/Resources/PrivacyInfo.xcprivacy b/packages/path_provider/path_provider_foundation/darwin/path_provider_foundation/Sources/path_provider_foundation/Resources/PrivacyInfo.xcprivacy
deleted file mode 100644
index a34b7e2e60c..00000000000
--- a/packages/path_provider/path_provider_foundation/darwin/path_provider_foundation/Sources/path_provider_foundation/Resources/PrivacyInfo.xcprivacy
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
-
- NSPrivacyTrackingDomains
-
- NSPrivacyAccessedAPITypes
-
- NSPrivacyCollectedDataTypes
-
- NSPrivacyTracking
-
-
-
diff --git a/packages/path_provider/path_provider_foundation/darwin/path_provider_foundation/Sources/path_provider_foundation/messages.g.swift b/packages/path_provider/path_provider_foundation/darwin/path_provider_foundation/Sources/path_provider_foundation/messages.g.swift
deleted file mode 100644
index 99df5a9cde4..00000000000
--- a/packages/path_provider/path_provider_foundation/darwin/path_provider_foundation/Sources/path_provider_foundation/messages.g.swift
+++ /dev/null
@@ -1,171 +0,0 @@
-// 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.
-// Autogenerated from Pigeon (v25.5.0), do not edit directly.
-// See also: https://pub.dev/packages/pigeon
-
-import Foundation
-
-#if os(iOS)
- import Flutter
-#elseif os(macOS)
- import FlutterMacOS
-#else
- #error("Unsupported platform.")
-#endif
-
-/// Error class for passing custom error details to Dart side.
-final class PigeonError: Error {
- let code: String
- let message: String?
- let details: Sendable?
-
- init(code: String, message: String?, details: Sendable?) {
- self.code = code
- self.message = message
- self.details = details
- }
-
- var localizedDescription: String {
- return
- "PigeonError(code: \(code), message: \(message ?? ""), details: \(details ?? "")"
- }
-}
-
-private func wrapResult(_ result: Any?) -> [Any?] {
- return [result]
-}
-
-private func wrapError(_ error: Any) -> [Any?] {
- if let pigeonError = error as? PigeonError {
- return [
- pigeonError.code,
- pigeonError.message,
- pigeonError.details,
- ]
- }
- if let flutterError = error as? FlutterError {
- return [
- flutterError.code,
- flutterError.message,
- flutterError.details,
- ]
- }
- return [
- "\(error)",
- "\(type(of: error))",
- "Stacktrace: \(Thread.callStackSymbols)",
- ]
-}
-
-private func isNullish(_ value: Any?) -> Bool {
- return value is NSNull || value == nil
-}
-
-private func nilOrValue(_ value: Any?) -> T? {
- if value is NSNull { return nil }
- return value as! T?
-}
-
-enum DirectoryType: Int {
- case applicationDocuments = 0
- case applicationSupport = 1
- case downloads = 2
- case library = 3
- case temp = 4
- case applicationCache = 5
-}
-
-private class MessagesPigeonCodecReader: FlutterStandardReader {
- override func readValue(ofType type: UInt8) -> Any? {
- switch type {
- case 129:
- let enumResultAsInt: Int? = nilOrValue(self.readValue() as! Int?)
- if let enumResultAsInt = enumResultAsInt {
- return DirectoryType(rawValue: enumResultAsInt)
- }
- return nil
- default:
- return super.readValue(ofType: type)
- }
- }
-}
-
-private class MessagesPigeonCodecWriter: FlutterStandardWriter {
- override func writeValue(_ value: Any) {
- if let value = value as? DirectoryType {
- super.writeByte(129)
- super.writeValue(value.rawValue)
- } else {
- super.writeValue(value)
- }
- }
-}
-
-private class MessagesPigeonCodecReaderWriter: FlutterStandardReaderWriter {
- override func reader(with data: Data) -> FlutterStandardReader {
- return MessagesPigeonCodecReader(data: data)
- }
-
- override func writer(with data: NSMutableData) -> FlutterStandardWriter {
- return MessagesPigeonCodecWriter(data: data)
- }
-}
-
-class MessagesPigeonCodec: FlutterStandardMessageCodec, @unchecked Sendable {
- static let shared = MessagesPigeonCodec(readerWriter: MessagesPigeonCodecReaderWriter())
-}
-
-/// Generated protocol from Pigeon that represents a handler of messages from Flutter.
-protocol PathProviderApi {
- func getDirectoryPath(type: DirectoryType) throws -> String?
- func getContainerPath(appGroupIdentifier: String) throws -> String?
-}
-
-/// Generated setup class from Pigeon to handle messages through the `binaryMessenger`.
-class PathProviderApiSetup {
- static var codec: FlutterStandardMessageCodec { MessagesPigeonCodec.shared }
- /// Sets up an instance of `PathProviderApi` to handle messages through the `binaryMessenger`.
- static func setUp(
- binaryMessenger: FlutterBinaryMessenger, api: PathProviderApi?,
- messageChannelSuffix: String = ""
- ) {
- let channelSuffix = messageChannelSuffix.count > 0 ? ".\(messageChannelSuffix)" : ""
- let getDirectoryPathChannel = FlutterBasicMessageChannel(
- name:
- "dev.flutter.pigeon.path_provider_foundation.PathProviderApi.getDirectoryPath\(channelSuffix)",
- binaryMessenger: binaryMessenger, codec: codec)
- if let api = api {
- getDirectoryPathChannel.setMessageHandler { message, reply in
- let args = message as! [Any?]
- let typeArg = args[0] as! DirectoryType
- do {
- let result = try api.getDirectoryPath(type: typeArg)
- reply(wrapResult(result))
- } catch {
- reply(wrapError(error))
- }
- }
- } else {
- getDirectoryPathChannel.setMessageHandler(nil)
- }
- let getContainerPathChannel = FlutterBasicMessageChannel(
- name:
- "dev.flutter.pigeon.path_provider_foundation.PathProviderApi.getContainerPath\(channelSuffix)",
- binaryMessenger: binaryMessenger, codec: codec)
- if let api = api {
- getContainerPathChannel.setMessageHandler { message, reply in
- let args = message as! [Any?]
- let appGroupIdentifierArg = args[0] as! String
- do {
- let result = try api.getContainerPath(appGroupIdentifier: appGroupIdentifierArg)
- reply(wrapResult(result))
- } catch {
- reply(wrapError(error))
- }
- }
- } else {
- getContainerPathChannel.setMessageHandler(nil)
- }
- }
-}
diff --git a/packages/path_provider/path_provider_foundation/example/build.yaml b/packages/path_provider/path_provider_foundation/example/build.yaml
new file mode 100644
index 00000000000..ef6f032afd8
--- /dev/null
+++ b/packages/path_provider/path_provider_foundation/example/build.yaml
@@ -0,0 +1,13 @@
+targets:
+ $default:
+ sources:
+ - $package$
+ - lib/$lib$
+ - lib/**.dart
+ - test/**.dart
+ - integration_test/**.dart
+ builders:
+ mockito|mockBuilder:
+ generate_for:
+ - test/**.dart
+ - integration_test/**.dart
diff --git a/packages/path_provider/path_provider_foundation/example/integration_test/path_provider_test.dart b/packages/path_provider/path_provider_foundation/example/integration_test/path_provider_test.dart
index 711334b3cde..2b33c952a84 100644
--- a/packages/path_provider/path_provider_foundation/example/integration_test/path_provider_test.dart
+++ b/packages/path_provider/path_provider_foundation/example/integration_test/path_provider_test.dart
@@ -5,69 +5,423 @@
import 'dart:io';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
-import 'package:path_provider_foundation/path_provider_foundation.dart';
+import 'package:mockito/annotations.dart';
+import 'package:mockito/mockito.dart';
+import 'package:objective_c/objective_c.dart';
+import 'package:path/path.dart' as p;
+import 'package:path_provider_foundation/src/ffi_bindings.g.dart';
+import 'package:path_provider_foundation/src/path_provider_foundation_real.dart';
import 'package:path_provider_platform_interface/path_provider_platform_interface.dart';
+import 'path_provider_test.mocks.dart';
+
+@GenerateNiceMocks(>[
+ MockSpec(),
+ MockSpec(),
+ MockSpec(),
+ MockSpec(),
+])
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
- testWidgets('getTemporaryDirectory', (WidgetTester tester) async {
- final PathProviderPlatform provider = PathProviderPlatform.instance;
- final String? result = await provider.getTemporaryPath();
- _verifySampleFile(result, 'temporaryDirectory');
- });
+ // This group contains standard integration tests that do end-to-end testing
+ // of the calls into the platform.
+ group('end-to-end', () {
+ testWidgets('getTemporaryDirectory', (WidgetTester tester) async {
+ final PathProviderPlatform provider = PathProviderPlatform.instance;
+ final String? result = await provider.getTemporaryPath();
+ _verifySampleFile(result, 'temporaryDirectory');
+ });
+
+ testWidgets('getApplicationDocumentsDirectory', (
+ WidgetTester tester,
+ ) async {
+ final PathProviderPlatform provider = PathProviderPlatform.instance;
+ final String? result = await provider.getApplicationDocumentsPath();
+ if (Platform.isMacOS) {
+ // _verifySampleFile causes hangs in driver when sandboxing is disabled
+ // because the path changes from an app specific directory to
+ // ~/Documents, which requires additional permissions to access on macOS.
+ // Instead, validate that a non-empty path was returned.
+ expect(result, isNotEmpty);
+ } else {
+ _verifySampleFile(result, 'applicationDocuments');
+ }
+ });
+
+ testWidgets('getApplicationSupportDirectory', (WidgetTester tester) async {
+ final PathProviderPlatform provider = PathProviderPlatform.instance;
+ final String? result = await provider.getApplicationSupportPath();
+ _verifySampleFile(result, 'applicationSupport');
+ });
+
+ testWidgets('getApplicationCacheDirectory', (WidgetTester tester) async {
+ final PathProviderPlatform provider = PathProviderPlatform.instance;
+ final String? result = await provider.getApplicationCachePath();
+ _verifySampleFile(result, 'applicationCache');
+ });
+
+ testWidgets('getLibraryDirectory', (WidgetTester tester) async {
+ final PathProviderPlatform provider = PathProviderPlatform.instance;
+ final String? result = await provider.getLibraryPath();
+ _verifySampleFile(result, 'library');
+ });
- testWidgets('getApplicationDocumentsDirectory', (WidgetTester tester) async {
- final PathProviderPlatform provider = PathProviderPlatform.instance;
- final String? result = await provider.getApplicationDocumentsPath();
- if (Platform.isMacOS) {
- // _verifySampleFile causes hangs in driver when sandboxing is disabled
- // because the path changes from an app specific directory to
- // ~/Documents, which requires additional permissions to access on macOS.
- // Instead, validate that a non-empty path was returned.
+ testWidgets('getDownloadsDirectory', (WidgetTester tester) async {
+ final PathProviderPlatform provider = PathProviderPlatform.instance;
+ final String? result = await provider.getDownloadsPath();
+ // _verifySampleFile causes hangs in driver for some reason, so just
+ // validate that a non-empty path was returned.
expect(result, isNotEmpty);
- } else {
- _verifySampleFile(result, 'applicationDocuments');
- }
- });
+ });
- testWidgets('getApplicationSupportDirectory', (WidgetTester tester) async {
- final PathProviderPlatform provider = PathProviderPlatform.instance;
- final String? result = await provider.getApplicationSupportPath();
- _verifySampleFile(result, 'applicationSupport');
+ testWidgets('getContainerDirectory', (WidgetTester tester) async {
+ if (Platform.isIOS) {
+ final PathProviderFoundation provider = PathProviderFoundation();
+ final String? result = await provider.getContainerPath(
+ appGroupIdentifier: 'group.flutter.appGroupTest',
+ );
+ _verifySampleFile(result, 'appGroup');
+ }
+ });
});
- testWidgets('getApplicationCacheDirectory', (WidgetTester tester) async {
- final PathProviderPlatform provider = PathProviderPlatform.instance;
- final String? result = await provider.getApplicationCachePath();
- _verifySampleFile(result, 'applicationCache');
- });
+ // This group contains tests that would normally be Dart unit tests in the
+ // test/ directory, but can't be because they use Objective-C types (NSURL,
+ // NSString, etc.) that aren't available in an actual unit test. For these
+ // tests, the platform is stubbed out.
+ group('unit', () {
+ final ValueVariant platformVariants =
+ ValueVariant({
+ FakePlatformProvider(isIOS: true),
+ FakePlatformProvider(isMacOS: true),
+ });
- testWidgets('getLibraryDirectory', (WidgetTester tester) async {
- final PathProviderPlatform provider = PathProviderPlatform.instance;
- final String? result = await provider.getLibraryPath();
- _verifySampleFile(result, 'library');
- });
+ // These tests use the actual filesystem, since an injectable filesystem
+ // would add a runtime dependency to the package, so everything is contained
+ // to a temporary directory.
+ late Directory testRoot;
- testWidgets('getDownloadsDirectory', (WidgetTester tester) async {
- final PathProviderPlatform provider = PathProviderPlatform.instance;
- final String? result = await provider.getDownloadsPath();
- // _verifySampleFile causes hangs in driver for some reason, so just
- // validate that a non-empty path was returned.
- expect(result, isNotEmpty);
- });
+ setUp(() async {
+ testRoot = Directory.systemTemp.createTempSync();
+ });
+
+ tearDown(() {
+ testRoot.deleteSync(recursive: true);
+ });
+
+ testWidgets('getTemporaryPath iOS', (_) async {
+ final MockFoundationFFI mockFfiLib = MockFoundationFFI();
+ final PathProviderFoundation pathProvider = PathProviderFoundation(
+ ffiLib: mockFfiLib,
+ platform: FakePlatformProvider(isIOS: true),
+ );
+
+ final String temporaryPath = p.join(testRoot.path, 'temporary', 'path');
+ when(
+ mockFfiLib.NSSearchPathForDirectoriesInDomains(
+ NSSearchPathDirectory.NSCachesDirectory,
+ NSSearchPathDomainMask.NSUserDomainMask,
+ true,
+ ),
+ ).thenReturn(_arrayWithString(temporaryPath));
+
+ final String? path = await pathProvider.getTemporaryPath();
+
+ expect(path, temporaryPath);
+ });
+
+ testWidgets('getTemporaryPath macOS', (_) async {
+ final MockFoundationFFI mockFfiLib = MockFoundationFFI();
+ final PathProviderFoundation pathProvider = PathProviderFoundation(
+ ffiLib: mockFfiLib,
+ platform: FakePlatformProvider(isMacOS: true),
+ );
+
+ final String temporaryPath = p.join(testRoot.path, 'temporary', 'path');
+ when(
+ mockFfiLib.NSSearchPathForDirectoriesInDomains(
+ NSSearchPathDirectory.NSCachesDirectory,
+ NSSearchPathDomainMask.NSUserDomainMask,
+ true,
+ ),
+ ).thenReturn(_arrayWithString(temporaryPath));
+
+ final String? path = await pathProvider.getTemporaryPath();
+
+ // On macOS, the bundle ID should be appended to the path.
+ expect(path, '$temporaryPath/com.example.pathProviderFoundationExample');
+ });
- testWidgets('getContainerDirectory', (WidgetTester tester) async {
- if (Platform.isIOS) {
- final PathProviderFoundation provider = PathProviderFoundation();
- final String? result = await provider.getContainerPath(
- appGroupIdentifier: 'group.flutter.appGroupTest',
+ testWidgets('getApplicationSupportPath iOS', (_) async {
+ final MockFoundationFFI mockFfiLib = MockFoundationFFI();
+ final PathProviderFoundation pathProvider = PathProviderFoundation(
+ ffiLib: mockFfiLib,
+ platform: FakePlatformProvider(isIOS: true),
);
- _verifySampleFile(result, 'appGroup');
- }
+
+ final String applicationSupportPath = p.join(
+ testRoot.path,
+ 'application',
+ 'support',
+ 'path',
+ );
+ when(
+ mockFfiLib.NSSearchPathForDirectoriesInDomains(
+ NSSearchPathDirectory.NSApplicationSupportDirectory,
+ NSSearchPathDomainMask.NSUserDomainMask,
+ true,
+ ),
+ ).thenReturn(_arrayWithString(applicationSupportPath));
+
+ final String? path = await pathProvider.getApplicationSupportPath();
+
+ expect(path, applicationSupportPath);
+ });
+
+ testWidgets('getApplicationSupportPath macOS', (_) async {
+ final MockFoundationFFI mockFfiLib = MockFoundationFFI();
+ final PathProviderFoundation pathProvider = PathProviderFoundation(
+ ffiLib: mockFfiLib,
+ platform: FakePlatformProvider(isMacOS: true),
+ );
+
+ final String applicationSupportPath = p.join(
+ testRoot.path,
+ 'application',
+ 'support',
+ 'path',
+ );
+ when(
+ mockFfiLib.NSSearchPathForDirectoriesInDomains(
+ NSSearchPathDirectory.NSApplicationSupportDirectory,
+ NSSearchPathDomainMask.NSUserDomainMask,
+ true,
+ ),
+ ).thenReturn(_arrayWithString(applicationSupportPath));
+
+ final String? path = await pathProvider.getApplicationSupportPath();
+
+ // On macOS, the bundle ID should be appended to the path.
+ expect(
+ path,
+ '$applicationSupportPath/com.example.pathProviderFoundationExample',
+ );
+ });
+
+ testWidgets(
+ 'getApplicationSupportPath creates the directory if necessary',
+ (_) async {
+ final MockFoundationFFI mockFfiLib = MockFoundationFFI();
+ final PathProviderFoundation pathProvider = PathProviderFoundation(
+ ffiLib: mockFfiLib,
+ platform: platformVariants.currentValue,
+ );
+
+ final String applicationSupportPath = p.join(
+ testRoot.path,
+ 'application',
+ 'support',
+ 'path',
+ );
+ when(
+ mockFfiLib.NSSearchPathForDirectoriesInDomains(
+ NSSearchPathDirectory.NSApplicationSupportDirectory,
+ NSSearchPathDomainMask.NSUserDomainMask,
+ true,
+ ),
+ ).thenReturn(_arrayWithString(applicationSupportPath));
+
+ final String? path = await pathProvider.getApplicationSupportPath();
+
+ expect(Directory(path!).existsSync(), isTrue);
+ },
+ variant: platformVariants,
+ );
+
+ testWidgets('getLibraryPath', (_) async {
+ final MockFoundationFFI mockFfiLib = MockFoundationFFI();
+ final PathProviderFoundation pathProvider = PathProviderFoundation(
+ ffiLib: mockFfiLib,
+ platform: platformVariants.currentValue,
+ );
+
+ final String libraryPath = p.join(testRoot.path, 'library', 'path');
+ when(
+ mockFfiLib.NSSearchPathForDirectoriesInDomains(
+ NSSearchPathDirectory.NSLibraryDirectory,
+ NSSearchPathDomainMask.NSUserDomainMask,
+ true,
+ ),
+ ).thenReturn(_arrayWithString(libraryPath));
+
+ final String? path = await pathProvider.getLibraryPath();
+
+ expect(path, libraryPath);
+ }, variant: platformVariants);
+
+ testWidgets('getApplicationDocumentsPath', (_) async {
+ final MockFoundationFFI mockFfiLib = MockFoundationFFI();
+ final PathProviderFoundation pathProvider = PathProviderFoundation(
+ ffiLib: mockFfiLib,
+ platform: platformVariants.currentValue,
+ );
+
+ final String applicationDocumentsPath = p.join(
+ testRoot.path,
+ 'application',
+ 'documents',
+ 'path',
+ );
+ when(
+ mockFfiLib.NSSearchPathForDirectoriesInDomains(
+ NSSearchPathDirectory.NSDocumentDirectory,
+ NSSearchPathDomainMask.NSUserDomainMask,
+ true,
+ ),
+ ).thenReturn(_arrayWithString(applicationDocumentsPath));
+
+ final String? path = await pathProvider.getApplicationDocumentsPath();
+
+ expect(path, applicationDocumentsPath);
+ }, variant: platformVariants);
+
+ testWidgets('getApplicationCachePath iOS', (_) async {
+ final MockFoundationFFI mockFfiLib = MockFoundationFFI();
+ final PathProviderFoundation pathProvider = PathProviderFoundation(
+ ffiLib: mockFfiLib,
+ platform: FakePlatformProvider(isIOS: true),
+ );
+
+ final String applicationCachePath = p.join(
+ testRoot.path,
+ 'application',
+ 'cache',
+ 'path',
+ );
+ when(
+ mockFfiLib.NSSearchPathForDirectoriesInDomains(
+ NSSearchPathDirectory.NSCachesDirectory,
+ NSSearchPathDomainMask.NSUserDomainMask,
+ true,
+ ),
+ ).thenReturn(_arrayWithString(applicationCachePath));
+
+ final String? path = await pathProvider.getApplicationCachePath();
+
+ expect(path, applicationCachePath);
+ });
+
+ testWidgets('getApplicationCachePath macOS', (_) async {
+ final MockFoundationFFI mockFfiLib = MockFoundationFFI();
+ final PathProviderFoundation pathProvider = PathProviderFoundation(
+ ffiLib: mockFfiLib,
+ platform: FakePlatformProvider(isMacOS: true),
+ );
+
+ final String applicationCachePath = p.join(
+ testRoot.path,
+ 'application',
+ 'cache',
+ 'path',
+ );
+ when(
+ mockFfiLib.NSSearchPathForDirectoriesInDomains(
+ NSSearchPathDirectory.NSCachesDirectory,
+ NSSearchPathDomainMask.NSUserDomainMask,
+ true,
+ ),
+ ).thenReturn(_arrayWithString(applicationCachePath));
+
+ final String? path = await pathProvider.getApplicationCachePath();
+
+ // On macOS, the bundle ID should be appended to the path.
+ expect(
+ path,
+ '$applicationCachePath/com.example.pathProviderFoundationExample',
+ );
+ });
+
+ testWidgets(
+ 'getApplicationCachePath creates the directory if necessary',
+ (_) async {
+ final MockFoundationFFI mockFfiLib = MockFoundationFFI();
+ final PathProviderFoundation pathProvider = PathProviderFoundation(
+ ffiLib: mockFfiLib,
+ platform: platformVariants.currentValue,
+ );
+
+ final String applicationCachePath = p.join(
+ testRoot.path,
+ 'application',
+ 'cache',
+ 'path',
+ );
+ when(
+ mockFfiLib.NSSearchPathForDirectoriesInDomains(
+ NSSearchPathDirectory.NSCachesDirectory,
+ NSSearchPathDomainMask.NSUserDomainMask,
+ true,
+ ),
+ ).thenReturn(_arrayWithString(applicationCachePath));
+
+ final String? path = await pathProvider.getApplicationCachePath();
+
+ expect(Directory(path!).existsSync(), isTrue);
+ },
+ variant: platformVariants,
+ );
+
+ testWidgets('getDownloadsPath', (_) async {
+ final MockFoundationFFI mockFfiLib = MockFoundationFFI();
+ final PathProviderFoundation pathProvider = PathProviderFoundation(
+ ffiLib: mockFfiLib,
+ platform: platformVariants.currentValue,
+ );
+
+ final String downloadsPath = p.join(testRoot.path, 'downloads', 'path');
+ when(
+ mockFfiLib.NSSearchPathForDirectoriesInDomains(
+ NSSearchPathDirectory.NSDownloadsDirectory,
+ NSSearchPathDomainMask.NSUserDomainMask,
+ true,
+ ),
+ ).thenReturn(_arrayWithString(downloadsPath));
+
+ final String? result = await pathProvider.getDownloadsPath();
+
+ expect(result, downloadsPath);
+ }, variant: platformVariants);
+
+ testWidgets('getContainerPath', (_) async {
+ final MockFoundationFFI mockFfiLib = MockFoundationFFI();
+ final MockNSFileManager mockFileManager = MockNSFileManager();
+ final PathProviderFoundation pathProvider = PathProviderFoundation(
+ ffiLib: mockFfiLib,
+ fileManagerProvider: () => mockFileManager,
+ platform: FakePlatformProvider(isIOS: true),
+ );
+
+ final String containerPath = p.join(testRoot.path, 'container', 'path');
+ final NSURL containerUrl = NSURL.fileURLWithPath(NSString(containerPath));
+ when(
+ mockFileManager.containerURLForSecurityApplicationGroupIdentifier(any),
+ ).thenReturn(containerUrl);
+
+ const String appGroupIdentifier = 'group.example.test';
+ final String? result = await pathProvider.getContainerPath(
+ appGroupIdentifier: appGroupIdentifier,
+ );
+
+ expect(result, containerPath);
+ });
});
}
+NSArray _arrayWithString(String s) {
+ return NSArray.arrayWithObject(NSString(s));
+}
+
/// Verify a file called [name] in [directoryPath] by recreating it with test
/// contents when necessary.
///
@@ -90,3 +444,17 @@ void _verifySampleFile(String? directoryPath, String name) {
expect(directory.listSync(), isNotEmpty);
file.deleteSync();
}
+
+/// Fake implementation of PathProviderPlatformProvider.
+class FakePlatformProvider implements PathProviderPlatformProvider {
+ FakePlatformProvider({this.isIOS = false, this.isMacOS = false})
+ : assert(isIOS != isMacOS);
+ @override
+ bool isIOS;
+
+ @override
+ bool isMacOS;
+
+ @override
+ String toString() => isIOS ? 'iOS' : 'macOS';
+}
diff --git a/packages/path_provider/path_provider_foundation/example/integration_test/path_provider_test.mocks.dart b/packages/path_provider/path_provider_foundation/example/integration_test/path_provider_test.mocks.dart
new file mode 100644
index 00000000000..e4cecfb1147
--- /dev/null
+++ b/packages/path_provider/path_provider_foundation/example/integration_test/path_provider_test.mocks.dart
@@ -0,0 +1,927 @@
+// Mocks generated by Mockito 5.4.6 from annotations
+// in path_provider_example/integration_test/path_provider_test.dart.
+// Do not manually edit this file.
+
+// ignore_for_file: no_leading_underscores_for_library_prefixes
+import 'dart:ffi' as _i5;
+
+import 'package:mockito/mockito.dart' as _i1;
+import 'package:mockito/src/dummies.dart' as _i4;
+import 'package:objective_c/objective_c.dart' as _i2;
+import 'package:path_provider_foundation/src/ffi_bindings.g.dart' as _i3;
+import 'package:path_provider_foundation/src/path_provider_foundation_real.dart'
+ as _i6;
+
+// ignore_for_file: type=lint
+// ignore_for_file: avoid_redundant_argument_values
+// ignore_for_file: avoid_setters_without_getters
+// ignore_for_file: comment_references
+// ignore_for_file: deprecated_member_use
+// ignore_for_file: deprecated_member_use_from_same_package
+// ignore_for_file: implementation_imports
+// ignore_for_file: invalid_use_of_visible_for_testing_member
+// ignore_for_file: must_be_immutable
+// ignore_for_file: prefer_const_constructors
+// ignore_for_file: unnecessary_parenthesis
+// ignore_for_file: camel_case_types
+// ignore_for_file: subtype_of_sealed_class
+
+class _FakeNSArray_0 extends _i1.SmartFake implements _i2.NSArray {
+ _FakeNSArray_0(Object parent, Invocation parentInvocation)
+ : super(parent, parentInvocation);
+}
+
+class _FakeNSString_1 extends _i1.SmartFake implements _i2.NSString {
+ _FakeNSString_1(Object parent, Invocation parentInvocation)
+ : super(parent, parentInvocation);
+}
+
+class _FakeObjCObjectBase_2 extends _i1.SmartFake
+ implements _i2.ObjCObjectBase {
+ _FakeObjCObjectBase_2(Object parent, Invocation parentInvocation)
+ : super(parent, parentInvocation);
+}
+
+class _FakeNSObject_3 extends _i1.SmartFake implements _i2.NSObject {
+ _FakeNSObject_3(Object parent, Invocation parentInvocation)
+ : super(parent, parentInvocation);
+}
+
+class _FakeNSMethodSignature_4 extends _i1.SmartFake
+ implements _i2.NSMethodSignature {
+ _FakeNSMethodSignature_4(Object parent, Invocation parentInvocation)
+ : super(parent, parentInvocation);
+}
+
+/// A class which mocks [FoundationFFI].
+///
+/// See the documentation for Mockito's code generation for more information.
+class MockFoundationFFI extends _i1.Mock implements _i3.FoundationFFI {
+ @override
+ _i2.NSArray NSSearchPathForDirectoriesInDomains(
+ _i3.NSSearchPathDirectory? directory,
+ _i3.NSSearchPathDomainMask? domainMask,
+ bool? expandTilde,
+ ) =>
+ (super.noSuchMethod(
+ Invocation.method(#NSSearchPathForDirectoriesInDomains, [
+ directory,
+ domainMask,
+ expandTilde,
+ ]),
+ returnValue: _FakeNSArray_0(
+ this,
+ Invocation.method(#NSSearchPathForDirectoriesInDomains, [
+ directory,
+ domainMask,
+ expandTilde,
+ ]),
+ ),
+ returnValueForMissingStub: _FakeNSArray_0(
+ this,
+ Invocation.method(#NSSearchPathForDirectoriesInDomains, [
+ directory,
+ domainMask,
+ expandTilde,
+ ]),
+ ),
+ )
+ as _i2.NSArray);
+}
+
+/// A class which mocks [NSBundle].
+///
+/// See the documentation for Mockito's code generation for more information.
+class MockNSBundle extends _i1.Mock implements _i3.NSBundle {
+ @override
+ _i2.NSString get debugDescription$1 =>
+ (super.noSuchMethod(
+ Invocation.getter(#debugDescription$1),
+ returnValue: _FakeNSString_1(
+ this,
+ Invocation.getter(#debugDescription$1),
+ ),
+ returnValueForMissingStub: _FakeNSString_1(
+ this,
+ Invocation.getter(#debugDescription$1),
+ ),
+ )
+ as _i2.NSString);
+
+ @override
+ _i2.NSString get description$1 =>
+ (super.noSuchMethod(
+ Invocation.getter(#description$1),
+ returnValue: _FakeNSString_1(
+ this,
+ Invocation.getter(#description$1),
+ ),
+ returnValueForMissingStub: _FakeNSString_1(
+ this,
+ Invocation.getter(#description$1),
+ ),
+ )
+ as _i2.NSString);
+
+ @override
+ int get hash$1 =>
+ (super.noSuchMethod(
+ Invocation.getter(#hash$1),
+ returnValue: 0,
+ returnValueForMissingStub: 0,
+ )
+ as int);
+
+ @override
+ bool get isProxy =>
+ (super.noSuchMethod(
+ Invocation.getter(#isProxy),
+ returnValue: false,
+ returnValueForMissingStub: false,
+ )
+ as bool);
+
+ @override
+ _i2.ObjCObjectBase get superclass$1 =>
+ (super.noSuchMethod(
+ Invocation.getter(#superclass$1),
+ returnValue: _FakeObjCObjectBase_2(
+ this,
+ Invocation.getter(#superclass$1),
+ ),
+ returnValueForMissingStub: _FakeObjCObjectBase_2(
+ this,
+ Invocation.getter(#superclass$1),
+ ),
+ )
+ as _i2.ObjCObjectBase);
+
+ @override
+ _i2.ObjCObjectRef get ref =>
+ (super.noSuchMethod(
+ Invocation.getter(#ref),
+ returnValue: _i4.dummyValue<_i2.ObjCObjectRef>(
+ this,
+ Invocation.getter(#ref),
+ ),
+ returnValueForMissingStub: _i4.dummyValue<_i2.ObjCObjectRef>(
+ this,
+ Invocation.getter(#ref),
+ ),
+ )
+ as _i2.ObjCObjectRef);
+
+ @override
+ _i2.NSObject autorelease() =>
+ (super.noSuchMethod(
+ Invocation.method(#autorelease, []),
+ returnValue: _FakeNSObject_3(
+ this,
+ Invocation.method(#autorelease, []),
+ ),
+ returnValueForMissingStub: _FakeNSObject_3(
+ this,
+ Invocation.method(#autorelease, []),
+ ),
+ )
+ as _i2.NSObject);
+
+ @override
+ _i2.ObjCObjectBase class$1() =>
+ (super.noSuchMethod(
+ Invocation.method(#class$1, []),
+ returnValue: _FakeObjCObjectBase_2(
+ this,
+ Invocation.method(#class$1, []),
+ ),
+ returnValueForMissingStub: _FakeObjCObjectBase_2(
+ this,
+ Invocation.method(#class$1, []),
+ ),
+ )
+ as _i2.ObjCObjectBase);
+
+ @override
+ bool conformsToProtocol$1(_i2.Protocol? aProtocol) =>
+ (super.noSuchMethod(
+ Invocation.method(#conformsToProtocol$1, [aProtocol]),
+ returnValue: false,
+ returnValueForMissingStub: false,
+ )
+ as bool);
+
+ @override
+ _i2.ObjCObjectBase copy() =>
+ (super.noSuchMethod(
+ Invocation.method(#copy, []),
+ returnValue: _FakeObjCObjectBase_2(
+ this,
+ Invocation.method(#copy, []),
+ ),
+ returnValueForMissingStub: _FakeObjCObjectBase_2(
+ this,
+ Invocation.method(#copy, []),
+ ),
+ )
+ as _i2.ObjCObjectBase);
+
+ @override
+ void dealloc() => super.noSuchMethod(
+ Invocation.method(#dealloc, []),
+ returnValueForMissingStub: null,
+ );
+
+ @override
+ void doesNotRecognizeSelector(_i5.Pointer<_i2.ObjCSelector>? aSelector) =>
+ super.noSuchMethod(
+ Invocation.method(#doesNotRecognizeSelector, [aSelector]),
+ returnValueForMissingStub: null,
+ );
+
+ @override
+ void forwardInvocation(_i2.NSInvocation? anInvocation) => super.noSuchMethod(
+ Invocation.method(#forwardInvocation, [anInvocation]),
+ returnValueForMissingStub: null,
+ );
+
+ @override
+ _i2.ObjCObjectBase forwardingTargetForSelector(
+ _i5.Pointer<_i2.ObjCSelector>? aSelector,
+ ) =>
+ (super.noSuchMethod(
+ Invocation.method(#forwardingTargetForSelector, [aSelector]),
+ returnValue: _FakeObjCObjectBase_2(
+ this,
+ Invocation.method(#forwardingTargetForSelector, [aSelector]),
+ ),
+ returnValueForMissingStub: _FakeObjCObjectBase_2(
+ this,
+ Invocation.method(#forwardingTargetForSelector, [aSelector]),
+ ),
+ )
+ as _i2.ObjCObjectBase);
+
+ @override
+ _i2.NSObject init() =>
+ (super.noSuchMethod(
+ Invocation.method(#init, []),
+ returnValue: _FakeNSObject_3(this, Invocation.method(#init, [])),
+ returnValueForMissingStub: _FakeNSObject_3(
+ this,
+ Invocation.method(#init, []),
+ ),
+ )
+ as _i2.NSObject);
+
+ @override
+ bool isEqual(_i2.ObjCObjectBase? object) =>
+ (super.noSuchMethod(
+ Invocation.method(#isEqual, [object]),
+ returnValue: false,
+ returnValueForMissingStub: false,
+ )
+ as bool);
+
+ @override
+ bool isKindOfClass(_i2.ObjCObjectBase? aClass) =>
+ (super.noSuchMethod(
+ Invocation.method(#isKindOfClass, [aClass]),
+ returnValue: false,
+ returnValueForMissingStub: false,
+ )
+ as bool);
+
+ @override
+ bool isMemberOfClass(_i2.ObjCObjectBase? aClass) =>
+ (super.noSuchMethod(
+ Invocation.method(#isMemberOfClass, [aClass]),
+ returnValue: false,
+ returnValueForMissingStub: false,
+ )
+ as bool);
+
+ @override
+ _i5.Pointer<_i5.NativeFunction<_i5.Void Function()>> methodForSelector(
+ _i5.Pointer<_i2.ObjCSelector>? aSelector,
+ ) =>
+ (super.noSuchMethod(
+ Invocation.method(#methodForSelector, [aSelector]),
+ returnValue: _i4.dummyValue<
+ _i5.Pointer<_i5.NativeFunction<_i5.Void Function()>>
+ >(this, Invocation.method(#methodForSelector, [aSelector])),
+ returnValueForMissingStub: _i4.dummyValue<
+ _i5.Pointer<_i5.NativeFunction<_i5.Void Function()>>
+ >(this, Invocation.method(#methodForSelector, [aSelector])),
+ )
+ as _i5.Pointer<_i5.NativeFunction<_i5.Void Function()>>);
+
+ @override
+ _i2.NSMethodSignature methodSignatureForSelector(
+ _i5.Pointer<_i2.ObjCSelector>? aSelector,
+ ) =>
+ (super.noSuchMethod(
+ Invocation.method(#methodSignatureForSelector, [aSelector]),
+ returnValue: _FakeNSMethodSignature_4(
+ this,
+ Invocation.method(#methodSignatureForSelector, [aSelector]),
+ ),
+ returnValueForMissingStub: _FakeNSMethodSignature_4(
+ this,
+ Invocation.method(#methodSignatureForSelector, [aSelector]),
+ ),
+ )
+ as _i2.NSMethodSignature);
+
+ @override
+ _i2.ObjCObjectBase mutableCopy() =>
+ (super.noSuchMethod(
+ Invocation.method(#mutableCopy, []),
+ returnValue: _FakeObjCObjectBase_2(
+ this,
+ Invocation.method(#mutableCopy, []),
+ ),
+ returnValueForMissingStub: _FakeObjCObjectBase_2(
+ this,
+ Invocation.method(#mutableCopy, []),
+ ),
+ )
+ as _i2.ObjCObjectBase);
+
+ @override
+ _i2.ObjCObjectBase performSelector(
+ _i5.Pointer<_i2.ObjCSelector>? aSelector,
+ ) =>
+ (super.noSuchMethod(
+ Invocation.method(#performSelector, [aSelector]),
+ returnValue: _FakeObjCObjectBase_2(
+ this,
+ Invocation.method(#performSelector, [aSelector]),
+ ),
+ returnValueForMissingStub: _FakeObjCObjectBase_2(
+ this,
+ Invocation.method(#performSelector, [aSelector]),
+ ),
+ )
+ as _i2.ObjCObjectBase);
+
+ @override
+ _i2.ObjCObjectBase performSelector$1(
+ _i5.Pointer<_i2.ObjCSelector>? aSelector, {
+ required _i2.ObjCObjectBase? withObject,
+ }) =>
+ (super.noSuchMethod(
+ Invocation.method(
+ #performSelector$1,
+ [aSelector],
+ {#withObject: withObject},
+ ),
+ returnValue: _FakeObjCObjectBase_2(
+ this,
+ Invocation.method(
+ #performSelector$1,
+ [aSelector],
+ {#withObject: withObject},
+ ),
+ ),
+ returnValueForMissingStub: _FakeObjCObjectBase_2(
+ this,
+ Invocation.method(
+ #performSelector$1,
+ [aSelector],
+ {#withObject: withObject},
+ ),
+ ),
+ )
+ as _i2.ObjCObjectBase);
+
+ @override
+ _i2.ObjCObjectBase performSelector$2(
+ _i5.Pointer<_i2.ObjCSelector>? aSelector, {
+ required _i2.ObjCObjectBase? withObject,
+ required _i2.ObjCObjectBase? withObject$1,
+ }) =>
+ (super.noSuchMethod(
+ Invocation.method(
+ #performSelector$2,
+ [aSelector],
+ {#withObject: withObject, #withObject$1: withObject$1},
+ ),
+ returnValue: _FakeObjCObjectBase_2(
+ this,
+ Invocation.method(
+ #performSelector$2,
+ [aSelector],
+ {#withObject: withObject, #withObject$1: withObject$1},
+ ),
+ ),
+ returnValueForMissingStub: _FakeObjCObjectBase_2(
+ this,
+ Invocation.method(
+ #performSelector$2,
+ [aSelector],
+ {#withObject: withObject, #withObject$1: withObject$1},
+ ),
+ ),
+ )
+ as _i2.ObjCObjectBase);
+
+ @override
+ void release() => super.noSuchMethod(
+ Invocation.method(#release, []),
+ returnValueForMissingStub: null,
+ );
+
+ @override
+ bool respondsToSelector(_i5.Pointer<_i2.ObjCSelector>? aSelector) =>
+ (super.noSuchMethod(
+ Invocation.method(#respondsToSelector, [aSelector]),
+ returnValue: false,
+ returnValueForMissingStub: false,
+ )
+ as bool);
+
+ @override
+ _i2.NSObject retain() =>
+ (super.noSuchMethod(
+ Invocation.method(#retain, []),
+ returnValue: _FakeNSObject_3(this, Invocation.method(#retain, [])),
+ returnValueForMissingStub: _FakeNSObject_3(
+ this,
+ Invocation.method(#retain, []),
+ ),
+ )
+ as _i2.NSObject);
+
+ @override
+ int retainCount() =>
+ (super.noSuchMethod(
+ Invocation.method(#retainCount, []),
+ returnValue: 0,
+ returnValueForMissingStub: 0,
+ )
+ as int);
+
+ @override
+ _i2.NSObject self$1() =>
+ (super.noSuchMethod(
+ Invocation.method(#self$1, []),
+ returnValue: _FakeNSObject_3(this, Invocation.method(#self$1, [])),
+ returnValueForMissingStub: _FakeNSObject_3(
+ this,
+ Invocation.method(#self$1, []),
+ ),
+ )
+ as _i2.NSObject);
+
+ @override
+ _i5.Pointer<_i2.NSZone> zone() =>
+ (super.noSuchMethod(
+ Invocation.method(#zone, []),
+ returnValue: _i4.dummyValue<_i5.Pointer<_i2.NSZone>>(
+ this,
+ Invocation.method(#zone, []),
+ ),
+ returnValueForMissingStub: _i4.dummyValue<_i5.Pointer<_i2.NSZone>>(
+ this,
+ Invocation.method(#zone, []),
+ ),
+ )
+ as _i5.Pointer<_i2.NSZone>);
+}
+
+/// A class which mocks [NSFileManager].
+///
+/// See the documentation for Mockito's code generation for more information.
+class MockNSFileManager extends _i1.Mock implements _i3.NSFileManager {
+ @override
+ _i2.NSString get debugDescription$1 =>
+ (super.noSuchMethod(
+ Invocation.getter(#debugDescription$1),
+ returnValue: _FakeNSString_1(
+ this,
+ Invocation.getter(#debugDescription$1),
+ ),
+ returnValueForMissingStub: _FakeNSString_1(
+ this,
+ Invocation.getter(#debugDescription$1),
+ ),
+ )
+ as _i2.NSString);
+
+ @override
+ _i2.NSString get description$1 =>
+ (super.noSuchMethod(
+ Invocation.getter(#description$1),
+ returnValue: _FakeNSString_1(
+ this,
+ Invocation.getter(#description$1),
+ ),
+ returnValueForMissingStub: _FakeNSString_1(
+ this,
+ Invocation.getter(#description$1),
+ ),
+ )
+ as _i2.NSString);
+
+ @override
+ int get hash$1 =>
+ (super.noSuchMethod(
+ Invocation.getter(#hash$1),
+ returnValue: 0,
+ returnValueForMissingStub: 0,
+ )
+ as int);
+
+ @override
+ bool get isProxy =>
+ (super.noSuchMethod(
+ Invocation.getter(#isProxy),
+ returnValue: false,
+ returnValueForMissingStub: false,
+ )
+ as bool);
+
+ @override
+ _i2.ObjCObjectBase get superclass$1 =>
+ (super.noSuchMethod(
+ Invocation.getter(#superclass$1),
+ returnValue: _FakeObjCObjectBase_2(
+ this,
+ Invocation.getter(#superclass$1),
+ ),
+ returnValueForMissingStub: _FakeObjCObjectBase_2(
+ this,
+ Invocation.getter(#superclass$1),
+ ),
+ )
+ as _i2.ObjCObjectBase);
+
+ @override
+ _i2.ObjCObjectRef get ref =>
+ (super.noSuchMethod(
+ Invocation.getter(#ref),
+ returnValue: _i4.dummyValue<_i2.ObjCObjectRef>(
+ this,
+ Invocation.getter(#ref),
+ ),
+ returnValueForMissingStub: _i4.dummyValue<_i2.ObjCObjectRef>(
+ this,
+ Invocation.getter(#ref),
+ ),
+ )
+ as _i2.ObjCObjectRef);
+
+ @override
+ _i2.NSURL? containerURLForSecurityApplicationGroupIdentifier(
+ _i2.NSString? groupIdentifier,
+ ) =>
+ (super.noSuchMethod(
+ Invocation.method(
+ #containerURLForSecurityApplicationGroupIdentifier,
+ [groupIdentifier],
+ ),
+ returnValueForMissingStub: null,
+ )
+ as _i2.NSURL?);
+
+ @override
+ _i2.NSObject autorelease() =>
+ (super.noSuchMethod(
+ Invocation.method(#autorelease, []),
+ returnValue: _FakeNSObject_3(
+ this,
+ Invocation.method(#autorelease, []),
+ ),
+ returnValueForMissingStub: _FakeNSObject_3(
+ this,
+ Invocation.method(#autorelease, []),
+ ),
+ )
+ as _i2.NSObject);
+
+ @override
+ _i2.ObjCObjectBase class$1() =>
+ (super.noSuchMethod(
+ Invocation.method(#class$1, []),
+ returnValue: _FakeObjCObjectBase_2(
+ this,
+ Invocation.method(#class$1, []),
+ ),
+ returnValueForMissingStub: _FakeObjCObjectBase_2(
+ this,
+ Invocation.method(#class$1, []),
+ ),
+ )
+ as _i2.ObjCObjectBase);
+
+ @override
+ bool conformsToProtocol$1(_i2.Protocol? aProtocol) =>
+ (super.noSuchMethod(
+ Invocation.method(#conformsToProtocol$1, [aProtocol]),
+ returnValue: false,
+ returnValueForMissingStub: false,
+ )
+ as bool);
+
+ @override
+ _i2.ObjCObjectBase copy() =>
+ (super.noSuchMethod(
+ Invocation.method(#copy, []),
+ returnValue: _FakeObjCObjectBase_2(
+ this,
+ Invocation.method(#copy, []),
+ ),
+ returnValueForMissingStub: _FakeObjCObjectBase_2(
+ this,
+ Invocation.method(#copy, []),
+ ),
+ )
+ as _i2.ObjCObjectBase);
+
+ @override
+ void dealloc() => super.noSuchMethod(
+ Invocation.method(#dealloc, []),
+ returnValueForMissingStub: null,
+ );
+
+ @override
+ void doesNotRecognizeSelector(_i5.Pointer<_i2.ObjCSelector>? aSelector) =>
+ super.noSuchMethod(
+ Invocation.method(#doesNotRecognizeSelector, [aSelector]),
+ returnValueForMissingStub: null,
+ );
+
+ @override
+ void forwardInvocation(_i2.NSInvocation? anInvocation) => super.noSuchMethod(
+ Invocation.method(#forwardInvocation, [anInvocation]),
+ returnValueForMissingStub: null,
+ );
+
+ @override
+ _i2.ObjCObjectBase forwardingTargetForSelector(
+ _i5.Pointer<_i2.ObjCSelector>? aSelector,
+ ) =>
+ (super.noSuchMethod(
+ Invocation.method(#forwardingTargetForSelector, [aSelector]),
+ returnValue: _FakeObjCObjectBase_2(
+ this,
+ Invocation.method(#forwardingTargetForSelector, [aSelector]),
+ ),
+ returnValueForMissingStub: _FakeObjCObjectBase_2(
+ this,
+ Invocation.method(#forwardingTargetForSelector, [aSelector]),
+ ),
+ )
+ as _i2.ObjCObjectBase);
+
+ @override
+ _i2.NSObject init() =>
+ (super.noSuchMethod(
+ Invocation.method(#init, []),
+ returnValue: _FakeNSObject_3(this, Invocation.method(#init, [])),
+ returnValueForMissingStub: _FakeNSObject_3(
+ this,
+ Invocation.method(#init, []),
+ ),
+ )
+ as _i2.NSObject);
+
+ @override
+ bool isEqual(_i2.ObjCObjectBase? object) =>
+ (super.noSuchMethod(
+ Invocation.method(#isEqual, [object]),
+ returnValue: false,
+ returnValueForMissingStub: false,
+ )
+ as bool);
+
+ @override
+ bool isKindOfClass(_i2.ObjCObjectBase? aClass) =>
+ (super.noSuchMethod(
+ Invocation.method(#isKindOfClass, [aClass]),
+ returnValue: false,
+ returnValueForMissingStub: false,
+ )
+ as bool);
+
+ @override
+ bool isMemberOfClass(_i2.ObjCObjectBase? aClass) =>
+ (super.noSuchMethod(
+ Invocation.method(#isMemberOfClass, [aClass]),
+ returnValue: false,
+ returnValueForMissingStub: false,
+ )
+ as bool);
+
+ @override
+ _i5.Pointer<_i5.NativeFunction<_i5.Void Function()>> methodForSelector(
+ _i5.Pointer<_i2.ObjCSelector>? aSelector,
+ ) =>
+ (super.noSuchMethod(
+ Invocation.method(#methodForSelector, [aSelector]),
+ returnValue: _i4.dummyValue<
+ _i5.Pointer<_i5.NativeFunction<_i5.Void Function()>>
+ >(this, Invocation.method(#methodForSelector, [aSelector])),
+ returnValueForMissingStub: _i4.dummyValue<
+ _i5.Pointer<_i5.NativeFunction<_i5.Void Function()>>
+ >(this, Invocation.method(#methodForSelector, [aSelector])),
+ )
+ as _i5.Pointer<_i5.NativeFunction<_i5.Void Function()>>);
+
+ @override
+ _i2.NSMethodSignature methodSignatureForSelector(
+ _i5.Pointer<_i2.ObjCSelector>? aSelector,
+ ) =>
+ (super.noSuchMethod(
+ Invocation.method(#methodSignatureForSelector, [aSelector]),
+ returnValue: _FakeNSMethodSignature_4(
+ this,
+ Invocation.method(#methodSignatureForSelector, [aSelector]),
+ ),
+ returnValueForMissingStub: _FakeNSMethodSignature_4(
+ this,
+ Invocation.method(#methodSignatureForSelector, [aSelector]),
+ ),
+ )
+ as _i2.NSMethodSignature);
+
+ @override
+ _i2.ObjCObjectBase mutableCopy() =>
+ (super.noSuchMethod(
+ Invocation.method(#mutableCopy, []),
+ returnValue: _FakeObjCObjectBase_2(
+ this,
+ Invocation.method(#mutableCopy, []),
+ ),
+ returnValueForMissingStub: _FakeObjCObjectBase_2(
+ this,
+ Invocation.method(#mutableCopy, []),
+ ),
+ )
+ as _i2.ObjCObjectBase);
+
+ @override
+ _i2.ObjCObjectBase performSelector(
+ _i5.Pointer<_i2.ObjCSelector>? aSelector,
+ ) =>
+ (super.noSuchMethod(
+ Invocation.method(#performSelector, [aSelector]),
+ returnValue: _FakeObjCObjectBase_2(
+ this,
+ Invocation.method(#performSelector, [aSelector]),
+ ),
+ returnValueForMissingStub: _FakeObjCObjectBase_2(
+ this,
+ Invocation.method(#performSelector, [aSelector]),
+ ),
+ )
+ as _i2.ObjCObjectBase);
+
+ @override
+ _i2.ObjCObjectBase performSelector$1(
+ _i5.Pointer<_i2.ObjCSelector>? aSelector, {
+ required _i2.ObjCObjectBase? withObject,
+ }) =>
+ (super.noSuchMethod(
+ Invocation.method(
+ #performSelector$1,
+ [aSelector],
+ {#withObject: withObject},
+ ),
+ returnValue: _FakeObjCObjectBase_2(
+ this,
+ Invocation.method(
+ #performSelector$1,
+ [aSelector],
+ {#withObject: withObject},
+ ),
+ ),
+ returnValueForMissingStub: _FakeObjCObjectBase_2(
+ this,
+ Invocation.method(
+ #performSelector$1,
+ [aSelector],
+ {#withObject: withObject},
+ ),
+ ),
+ )
+ as _i2.ObjCObjectBase);
+
+ @override
+ _i2.ObjCObjectBase performSelector$2(
+ _i5.Pointer<_i2.ObjCSelector>? aSelector, {
+ required _i2.ObjCObjectBase? withObject,
+ required _i2.ObjCObjectBase? withObject$1,
+ }) =>
+ (super.noSuchMethod(
+ Invocation.method(
+ #performSelector$2,
+ [aSelector],
+ {#withObject: withObject, #withObject$1: withObject$1},
+ ),
+ returnValue: _FakeObjCObjectBase_2(
+ this,
+ Invocation.method(
+ #performSelector$2,
+ [aSelector],
+ {#withObject: withObject, #withObject$1: withObject$1},
+ ),
+ ),
+ returnValueForMissingStub: _FakeObjCObjectBase_2(
+ this,
+ Invocation.method(
+ #performSelector$2,
+ [aSelector],
+ {#withObject: withObject, #withObject$1: withObject$1},
+ ),
+ ),
+ )
+ as _i2.ObjCObjectBase);
+
+ @override
+ void release() => super.noSuchMethod(
+ Invocation.method(#release, []),
+ returnValueForMissingStub: null,
+ );
+
+ @override
+ bool respondsToSelector(_i5.Pointer<_i2.ObjCSelector>? aSelector) =>
+ (super.noSuchMethod(
+ Invocation.method(#respondsToSelector, [aSelector]),
+ returnValue: false,
+ returnValueForMissingStub: false,
+ )
+ as bool);
+
+ @override
+ _i2.NSObject retain() =>
+ (super.noSuchMethod(
+ Invocation.method(#retain, []),
+ returnValue: _FakeNSObject_3(this, Invocation.method(#retain, [])),
+ returnValueForMissingStub: _FakeNSObject_3(
+ this,
+ Invocation.method(#retain, []),
+ ),
+ )
+ as _i2.NSObject);
+
+ @override
+ int retainCount() =>
+ (super.noSuchMethod(
+ Invocation.method(#retainCount, []),
+ returnValue: 0,
+ returnValueForMissingStub: 0,
+ )
+ as int);
+
+ @override
+ _i2.NSObject self$1() =>
+ (super.noSuchMethod(
+ Invocation.method(#self$1, []),
+ returnValue: _FakeNSObject_3(this, Invocation.method(#self$1, [])),
+ returnValueForMissingStub: _FakeNSObject_3(
+ this,
+ Invocation.method(#self$1, []),
+ ),
+ )
+ as _i2.NSObject);
+
+ @override
+ _i5.Pointer<_i2.NSZone> zone() =>
+ (super.noSuchMethod(
+ Invocation.method(#zone, []),
+ returnValue: _i4.dummyValue<_i5.Pointer<_i2.NSZone>>(
+ this,
+ Invocation.method(#zone, []),
+ ),
+ returnValueForMissingStub: _i4.dummyValue<_i5.Pointer<_i2.NSZone>>(
+ this,
+ Invocation.method(#zone, []),
+ ),
+ )
+ as _i5.Pointer<_i2.NSZone>);
+}
+
+/// A class which mocks [PathProviderPlatformProvider].
+///
+/// See the documentation for Mockito's code generation for more information.
+class MockPathProviderPlatformProvider extends _i1.Mock
+ implements _i6.PathProviderPlatformProvider {
+ @override
+ bool get isIOS =>
+ (super.noSuchMethod(
+ Invocation.getter(#isIOS),
+ returnValue: false,
+ returnValueForMissingStub: false,
+ )
+ as bool);
+
+ @override
+ bool get isMacOS =>
+ (super.noSuchMethod(
+ Invocation.getter(#isMacOS),
+ returnValue: false,
+ returnValueForMissingStub: false,
+ )
+ as bool);
+}
diff --git a/packages/path_provider/path_provider_foundation/example/pubspec.yaml b/packages/path_provider/path_provider_foundation/example/pubspec.yaml
index 6c2ff307a92..981f5e8d3a4 100644
--- a/packages/path_provider/path_provider_foundation/example/pubspec.yaml
+++ b/packages/path_provider/path_provider_foundation/example/pubspec.yaml
@@ -3,8 +3,8 @@ description: Demonstrates how to use the path_provider plugin.
publish_to: none
environment:
- sdk: ^3.7.0
- flutter: ">=3.29.0"
+ sdk: ^3.6.0
+ flutter: ">=3.27.0"
dependencies:
flutter:
@@ -19,10 +19,14 @@ dependencies:
path_provider_platform_interface: ^2.1.0
dev_dependencies:
+ build_runner: ^2.6.0
flutter_test:
sdk: flutter
integration_test:
sdk: flutter
+ mockito: ^5.4.4
+ objective_c: ^8.0.0
+ path: ^1.8.0
flutter:
uses-material-design: true
diff --git a/packages/path_provider/path_provider_foundation/ffigen_config.yaml b/packages/path_provider/path_provider_foundation/ffigen_config.yaml
new file mode 100644
index 00000000000..f7fdd6463c4
--- /dev/null
+++ b/packages/path_provider/path_provider_foundation/ffigen_config.yaml
@@ -0,0 +1,36 @@
+
+name: FoundationFFI
+description: Bindings for NSFileManager.
+language: objc
+output:
+ bindings: 'lib/src/ffi_bindings.g.dart'
+headers:
+ entry-points:
+ - '$MACOS_SDK/System/Library/Frameworks/Foundation.framework/Headers/Foundation.h'
+exclude-all-by-default: true
+include-transitive-objc-categories: false
+objc-interfaces:
+ include:
+ - 'NSBundle'
+ - 'NSFileManager'
+ - 'NSURL'
+ member-filter:
+ NSBundle:
+ include:
+ - "bundleIdentifier"
+ - "mainBundle"
+ NSFileManager:
+ include:
+ - "containerURLForSecurityApplicationGroupIdentifier:"
+ - "defaultManager"
+ NSURL:
+ include:
+ - "fileURLWithPath:"
+ - "URLByAppendingPathComponent:"
+objc-categories:
+ include:
+ # For URLByAppendingPathComponent:
+ - 'NSURLPathUtilities'
+functions:
+ include:
+ - 'NSSearchPathForDirectoriesInDomains'
diff --git a/packages/path_provider/path_provider_foundation/lib/messages.g.dart b/packages/path_provider/path_provider_foundation/lib/messages.g.dart
deleted file mode 100644
index 892b73bd1fe..00000000000
--- a/packages/path_provider/path_provider_foundation/lib/messages.g.dart
+++ /dev/null
@@ -1,140 +0,0 @@
-// 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.
-// Autogenerated from Pigeon (v25.5.0), do not edit directly.
-// See also: https://pub.dev/packages/pigeon
-// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import, no_leading_underscores_for_local_identifiers
-
-import 'dart:async';
-import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List;
-
-import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer;
-import 'package:flutter/services.dart';
-
-PlatformException _createConnectionError(String channelName) {
- return PlatformException(
- code: 'channel-error',
- message: 'Unable to establish connection on channel: "$channelName".',
- );
-}
-
-List