[path_provider] Switch to Pigeon for macOS (#6635)

* [path_provider] Switch to Pigeon for macOS

Converts from direct method channel use to the new experimental Swift
generator. Also switches from a one-call-per-method approach to a single
method with an enum, since it's easy to do that in a maintainable way
with Pigeon (unlike manual method channels, where keeping enum indexes
in sync across the language boundary is error-prone, and requires manual
int/enum conversion).

* Format

* Analyzer

* Update to latest version of Pigeon
diff --git a/packages/path_provider/path_provider_macos/CHANGELOG.md b/packages/path_provider/path_provider_macos/CHANGELOG.md
index 61727e4..211a59b 100644
--- a/packages/path_provider/path_provider_macos/CHANGELOG.md
+++ b/packages/path_provider/path_provider_macos/CHANGELOG.md
@@ -1,5 +1,6 @@
-## NEXT
+## 2.0.7
 
+* Switches platform channel implementation to Pigeon.
 * Updates minimum Flutter version to 2.10.
 
 ## 2.0.6
diff --git a/packages/path_provider/path_provider_macos/example/macos/RunnerTests/RunnerTests.swift b/packages/path_provider/path_provider_macos/example/macos/RunnerTests/RunnerTests.swift
index 35704cd..1fb4677 100644
--- a/packages/path_provider/path_provider_macos/example/macos/RunnerTests/RunnerTests.swift
+++ b/packages/path_provider/path_provider_macos/example/macos/RunnerTests/RunnerTests.swift
@@ -4,18 +4,12 @@
 
 import FlutterMacOS
 import XCTest
-import path_provider_macos
+@testable import path_provider_macos
 
 class RunnerTests: XCTestCase {
   func testGetTemporaryDirectory() throws {
     let plugin = PathProviderPlugin()
-    var path: String?
-    plugin.handle(
-      FlutterMethodCall(methodName: "getTemporaryDirectory", arguments: nil),
-      result: { (result: Any?) -> Void in
-        path = result as? String
-
-      })
+    let path = plugin.getDirectoryPath(type: .temp)
     XCTAssertEqual(
       path,
       NSSearchPathForDirectoriesInDomains(
@@ -27,13 +21,7 @@
 
   func testGetApplicationDocumentsDirectory() throws {
     let plugin = PathProviderPlugin()
-    var path: String?
-    plugin.handle(
-      FlutterMethodCall(methodName: "getApplicationDocumentsDirectory", arguments: nil),
-      result: { (result: Any?) -> Void in
-        path = result as? String
-
-      })
+    let path = plugin.getDirectoryPath(type: .applicationDocuments)
     XCTAssertEqual(
       path,
       NSSearchPathForDirectoriesInDomains(
@@ -45,13 +33,7 @@
 
   func testGetApplicationSupportDirectory() throws {
     let plugin = PathProviderPlugin()
-    var path: String?
-    plugin.handle(
-      FlutterMethodCall(methodName: "getApplicationSupportDirectory", arguments: nil),
-      result: { (result: Any?) -> Void in
-        path = result as? String
-
-      })
+    let path = plugin.getDirectoryPath(type: .applicationSupport)
     // The application support directory path should be the system application support
     // path with an added subdirectory based on the app name.
     XCTAssert(
@@ -66,13 +48,7 @@
 
   func testGetLibraryDirectory() throws {
     let plugin = PathProviderPlugin()
-    var path: String?
-    plugin.handle(
-      FlutterMethodCall(methodName: "getLibraryDirectory", arguments: nil),
-      result: { (result: Any?) -> Void in
-        path = result as? String
-
-      })
+    let path = plugin.getDirectoryPath(type: .library)
     XCTAssertEqual(
       path,
       NSSearchPathForDirectoriesInDomains(
@@ -84,13 +60,7 @@
 
   func testGetDownloadsDirectory() throws {
     let plugin = PathProviderPlugin()
-    var path: String?
-    plugin.handle(
-      FlutterMethodCall(methodName: "getDownloadsDirectory", arguments: nil),
-      result: { (result: Any?) -> Void in
-        path = result as? String
-
-      })
+    let path = plugin.getDirectoryPath(type: .downloads)
     XCTAssertEqual(
       path,
       NSSearchPathForDirectoriesInDomains(
diff --git a/packages/path_provider/path_provider_macos/lib/messages.g.dart b/packages/path_provider/path_provider_macos/lib/messages.g.dart
new file mode 100644
index 0000000..81a9cd5
--- /dev/null
+++ b/packages/path_provider/path_provider_macos/lib/messages.g.dart
@@ -0,0 +1,52 @@
+// 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 (v5.0.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
+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';
+
+enum DirectoryType {
+  applicationDocuments,
+  applicationSupport,
+  downloads,
+  library,
+  temp,
+}
+
+class PathProviderApi {
+  /// Constructor for [PathProviderApi].  The [binaryMessenger] named argument is
+  /// available for dependency injection.  If it is left null, the default
+  /// BinaryMessenger will be used which routes to the host platform.
+  PathProviderApi({BinaryMessenger? binaryMessenger})
+      : _binaryMessenger = binaryMessenger;
+  final BinaryMessenger? _binaryMessenger;
+
+  static const MessageCodec<Object?> codec = StandardMessageCodec();
+
+  Future<String?> getDirectoryPath(DirectoryType arg_type) async {
+    final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
+        'dev.flutter.pigeon.PathProviderApi.getDirectoryPath', codec,
+        binaryMessenger: _binaryMessenger);
+    final List<Object?>? replyList =
+        await channel.send(<Object?>[arg_type.index]) as List<Object?>?;
+    if (replyList == null) {
+      throw PlatformException(
+        code: 'channel-error',
+        message: 'Unable to establish connection on channel.',
+      );
+    } else if (replyList.length > 1) {
+      throw PlatformException(
+        code: replyList[0]! as String,
+        message: replyList[1] as String?,
+        details: replyList[2],
+      );
+    } else {
+      return (replyList[0] as String?);
+    }
+  }
+}
diff --git a/packages/path_provider/path_provider_macos/lib/path_provider_macos.dart b/packages/path_provider/path_provider_macos/lib/path_provider_macos.dart
index 5dc3176..7563bea 100644
--- a/packages/path_provider/path_provider_macos/lib/path_provider_macos.dart
+++ b/packages/path_provider/path_provider_macos/lib/path_provider_macos.dart
@@ -4,16 +4,13 @@
 
 import 'dart:io';
 
-import 'package:flutter/foundation.dart' show visibleForTesting;
-import 'package:flutter/services.dart';
 import 'package:path_provider_platform_interface/path_provider_platform_interface.dart';
 
+import 'messages.g.dart';
+
 /// The macOS implementation of [PathProviderPlatform].
 class PathProviderMacOS extends PathProviderPlatform {
-  /// The method channel used to interact with the native platform.
-  @visibleForTesting
-  MethodChannel methodChannel =
-      const MethodChannel('plugins.flutter.io/path_provider_macos');
+  final PathProviderApi _pathProvider = PathProviderApi();
 
   /// Registers this class as the default instance of [PathProviderPlatform]
   static void registerWith() {
@@ -22,13 +19,13 @@
 
   @override
   Future<String?> getTemporaryPath() {
-    return methodChannel.invokeMethod<String>('getTemporaryDirectory');
+    return _pathProvider.getDirectoryPath(DirectoryType.temp);
   }
 
   @override
   Future<String?> getApplicationSupportPath() async {
-    final String? path = await methodChannel
-        .invokeMethod<String>('getApplicationSupportDirectory');
+    final String? path =
+        await _pathProvider.getDirectoryPath(DirectoryType.applicationSupport);
     if (path != null) {
       // Ensure the directory exists before returning it, for consistency with
       // other platforms.
@@ -39,13 +36,12 @@
 
   @override
   Future<String?> getLibraryPath() {
-    return methodChannel.invokeMethod<String>('getLibraryDirectory');
+    return _pathProvider.getDirectoryPath(DirectoryType.library);
   }
 
   @override
   Future<String?> getApplicationDocumentsPath() {
-    return methodChannel
-        .invokeMethod<String>('getApplicationDocumentsDirectory');
+    return _pathProvider.getDirectoryPath(DirectoryType.applicationDocuments);
   }
 
   @override
@@ -67,6 +63,6 @@
 
   @override
   Future<String?> getDownloadsPath() {
-    return methodChannel.invokeMethod<String>('getDownloadsDirectory');
+    return _pathProvider.getDirectoryPath(DirectoryType.downloads);
   }
 }
diff --git a/packages/path_provider/path_provider_macos/macos/Classes/PathProviderPlugin.swift b/packages/path_provider/path_provider_macos/macos/Classes/PathProviderPlugin.swift
index e138eee..9d43ce7 100644
--- a/packages/path_provider/path_provider_macos/macos/Classes/PathProviderPlugin.swift
+++ b/packages/path_provider/path_provider_macos/macos/Classes/PathProviderPlugin.swift
@@ -5,39 +5,41 @@
 import FlutterMacOS
 import Foundation
 
-public class PathProviderPlugin: NSObject, FlutterPlugin {
+public class PathProviderPlugin: NSObject, FlutterPlugin, PathProviderApi {
   public static func register(with registrar: FlutterPluginRegistrar) {
-    let channel = FlutterMethodChannel(
-      name: "plugins.flutter.io/path_provider_macos",
-      binaryMessenger: registrar.messenger)
     let instance = PathProviderPlugin()
-    registrar.addMethodCallDelegate(instance, channel: channel)
+    PathProviderApiSetup.setUp(binaryMessenger: registrar.messenger, api: instance)
   }
 
-  public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
-    switch call.method {
-    case "getTemporaryDirectory":
-      result(getDirectory(ofType: FileManager.SearchPathDirectory.cachesDirectory))
-    case "getApplicationDocumentsDirectory":
-      result(getDirectory(ofType: FileManager.SearchPathDirectory.documentDirectory))
-    case "getApplicationSupportDirectory":
-      var path = getDirectory(ofType: FileManager.SearchPathDirectory.applicationSupportDirectory)
+  func getDirectoryPath(type: DirectoryType) -> String? {
+    var path = getDirectory(ofType: fileManagerDirectoryForType(type))
+    if type == .applicationSupport {
       if let basePath = path {
         let basePathURL = URL.init(fileURLWithPath: basePath)
         path = basePathURL.appendingPathComponent(Bundle.main.bundleIdentifier!).path
       }
-      result(path)
-    case "getLibraryDirectory":
-      result(getDirectory(ofType: FileManager.SearchPathDirectory.libraryDirectory))
-    case "getDownloadsDirectory":
-      result(getDirectory(ofType: FileManager.SearchPathDirectory.downloadsDirectory))
-    default:
-      result(FlutterMethodNotImplemented)
     }
+    return path
   }
 }
 
-/// Returns the user-domain director of the given type.
+/// Returns the FileManager constant corresponding to the given type.
+private func fileManagerDirectoryForType(_ type: DirectoryType) -> FileManager.SearchPathDirectory {
+  switch type {
+    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,
diff --git a/packages/path_provider/path_provider_macos/macos/Classes/messages.g.swift b/packages/path_provider/path_provider_macos/macos/Classes/messages.g.swift
new file mode 100644
index 0000000..08ab62a
--- /dev/null
+++ b/packages/path_provider/path_provider_macos/macos/Classes/messages.g.swift
@@ -0,0 +1,60 @@
+// 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 (v5.0.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
+
+
+/// Generated class from Pigeon.
+
+enum DirectoryType: Int {
+  case applicationDocuments = 0
+  case applicationSupport = 1
+  case downloads = 2
+  case library = 3
+  case temp = 4
+}
+/// Generated protocol from Pigeon that represents a handler of messages from Flutter.
+protocol PathProviderApi {
+  func getDirectoryPath(type: DirectoryType) -> String?
+}
+
+/// Generated setup class from Pigeon to handle messages through the `binaryMessenger`.
+class PathProviderApiSetup {
+  /// The codec used by PathProviderApi.
+  /// Sets up an instance of `PathProviderApi` to handle messages through the `binaryMessenger`.
+  static func setUp(binaryMessenger: FlutterBinaryMessenger, api: PathProviderApi?) {
+    let getDirectoryPathChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.PathProviderApi.getDirectoryPath", binaryMessenger: binaryMessenger)
+    if let api = api {
+      getDirectoryPathChannel.setMessageHandler { message, reply in
+        let args = message as! [Any?]
+        let typeArg = DirectoryType(rawValue: args[0] as! Int)!
+        let result = api.getDirectoryPath(type: typeArg)
+        reply(wrapResult(result))
+      }
+    } else {
+      getDirectoryPathChannel.setMessageHandler(nil)
+    }
+  }
+}
+
+private func wrapResult(_ result: Any?) -> [Any?] {
+  return [result]
+}
+
+private func wrapError(_ error: FlutterError) -> [Any?] {
+  return [
+    error.code,
+    error.message,
+    error.details
+  ]
+}
diff --git a/packages/path_provider/path_provider_macos/pigeons/copyright.txt b/packages/path_provider/path_provider_macos/pigeons/copyright.txt
new file mode 100644
index 0000000..1236b63
--- /dev/null
+++ b/packages/path_provider/path_provider_macos/pigeons/copyright.txt
@@ -0,0 +1,3 @@
+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.
diff --git a/packages/path_provider/path_provider_macos/pigeons/messages.dart b/packages/path_provider/path_provider_macos/pigeons/messages.dart
new file mode 100644
index 0000000..8c82ab4
--- /dev/null
+++ b/packages/path_provider/path_provider_macos/pigeons/messages.dart
@@ -0,0 +1,24 @@
+// 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 'package:pigeon/pigeon.dart';
+
+@ConfigurePigeon(PigeonOptions(
+  input: 'pigeons/messages.dart',
+  swiftOut: 'macos/Classes/messages.g.swift',
+  dartOut: 'lib/messages.g.dart',
+  dartTestOut: 'test/messages_test.g.dart',
+  copyrightHeader: 'pigeons/copyright.txt',
+))
+enum DirectoryType {
+  applicationDocuments,
+  applicationSupport,
+  downloads,
+  library,
+  temp,
+}
+
+@HostApi(dartHostTestHandler: 'TestPathProviderApi')
+abstract class PathProviderApi {
+  String? getDirectoryPath(DirectoryType type);
+}
diff --git a/packages/path_provider/path_provider_macos/pubspec.yaml b/packages/path_provider/path_provider_macos/pubspec.yaml
index 289e131..2722d26 100644
--- a/packages/path_provider/path_provider_macos/pubspec.yaml
+++ b/packages/path_provider/path_provider_macos/pubspec.yaml
@@ -2,7 +2,7 @@
 description: macOS implementation of the path_provider plugin
 repository: https://github.com/flutter/plugins/tree/main/packages/path_provider/path_provider_macos
 issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22
-version: 2.0.6
+version: 2.0.7
 
 environment:
   sdk: ">=2.12.0 <3.0.0"
@@ -22,6 +22,9 @@
   path_provider_platform_interface: ^2.0.1
 
 dev_dependencies:
+  build_runner: ^2.3.2
   flutter_test:
     sdk: flutter
+  mockito: ^5.3.2
   path: ^1.8.0
+  pigeon: ^5.0.0
diff --git a/packages/path_provider/path_provider_macos/test/messages_test.g.dart b/packages/path_provider/path_provider_macos/test/messages_test.g.dart
new file mode 100644
index 0000000..b0f3c1a
--- /dev/null
+++ b/packages/path_provider/path_provider_macos/test/messages_test.g.dart
@@ -0,0 +1,44 @@
+// 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 (v5.0.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, unnecessary_import
+// ignore_for_file: avoid_relative_lib_imports
+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';
+import 'package:flutter_test/flutter_test.dart';
+
+import 'package:path_provider_macos/messages.g.dart';
+
+abstract class TestPathProviderApi {
+  static const MessageCodec<Object?> codec = StandardMessageCodec();
+
+  String? getDirectoryPath(DirectoryType type);
+
+  static void setup(TestPathProviderApi? api,
+      {BinaryMessenger? binaryMessenger}) {
+    {
+      final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
+          'dev.flutter.pigeon.PathProviderApi.getDirectoryPath', codec,
+          binaryMessenger: binaryMessenger);
+      if (api == null) {
+        channel.setMockMessageHandler(null);
+      } else {
+        channel.setMockMessageHandler((Object? message) async {
+          assert(message != null,
+              'Argument for dev.flutter.pigeon.PathProviderApi.getDirectoryPath was null.');
+          final List<Object?> args = (message as List<Object?>?)!;
+          final DirectoryType? arg_type =
+              args[0] == null ? null : DirectoryType.values[args[0] as int];
+          assert(arg_type != null,
+              'Argument for dev.flutter.pigeon.PathProviderApi.getDirectoryPath was null, expected non-null DirectoryType.');
+          final String? output = api.getDirectoryPath(arg_type!);
+          return <Object?>[output];
+        });
+      }
+    }
+  }
+}
diff --git a/packages/path_provider/path_provider_macos/test/path_provider_macos_test.dart b/packages/path_provider/path_provider_macos/test/path_provider_macos_test.dart
index 7e783aa..1bd6447 100644
--- a/packages/path_provider/path_provider_macos/test/path_provider_macos_test.dart
+++ b/packages/path_provider/path_provider_macos/test/path_provider_macos_test.dart
@@ -4,61 +4,33 @@
 
 import 'dart:io';
 
-import 'package:flutter/services.dart';
 import 'package:flutter_test/flutter_test.dart';
+import 'package:mockito/annotations.dart';
+import 'package:mockito/mockito.dart';
 import 'package:path/path.dart' as p;
+import 'package:path_provider_macos/messages.g.dart';
 import 'package:path_provider_macos/path_provider_macos.dart';
 
+import 'messages_test.g.dart';
+import 'path_provider_macos_test.mocks.dart';
+
+@GenerateMocks(<Type>[TestPathProviderApi])
 void main() {
   TestWidgetsFlutterBinding.ensureInitialized();
 
   group('PathProviderMacOS', () {
     late PathProviderMacOS pathProvider;
-    late List<MethodCall> log;
+    late MockTestPathProviderApi mockApi;
     // These unit 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;
 
-    late String temporaryPath;
-    late String applicationSupportPath;
-    late String libraryPath;
-    late String applicationDocumentsPath;
-    late String downloadsPath;
-
     setUp(() async {
-      pathProvider = PathProviderMacOS();
-
       testRoot = Directory.systemTemp.createTempSync();
-      final String basePath = testRoot.path;
-      temporaryPath = p.join(basePath, 'temporary', 'path');
-      applicationSupportPath =
-          p.join(basePath, 'application', 'support', 'path');
-      libraryPath = p.join(basePath, 'library', 'path');
-      applicationDocumentsPath =
-          p.join(basePath, 'application', 'documents', 'path');
-      downloadsPath = p.join(basePath, 'downloads', 'path');
-
-      log = <MethodCall>[];
-      TestDefaultBinaryMessengerBinding.instance!.defaultBinaryMessenger
-          .setMockMethodCallHandler(pathProvider.methodChannel,
-              (MethodCall methodCall) async {
-        log.add(methodCall);
-        switch (methodCall.method) {
-          case 'getTemporaryDirectory':
-            return temporaryPath;
-          case 'getApplicationSupportDirectory':
-            return applicationSupportPath;
-          case 'getLibraryDirectory':
-            return libraryPath;
-          case 'getApplicationDocumentsDirectory':
-            return applicationDocumentsPath;
-          case 'getDownloadsDirectory':
-            return downloadsPath;
-          default:
-            return null;
-        }
-      });
+      pathProvider = PathProviderMacOS();
+      mockApi = MockTestPathProviderApi();
+      TestPathProviderApi.setup(mockApi);
     });
 
     tearDown(() {
@@ -66,57 +38,71 @@
     });
 
     test('getTemporaryPath', () async {
+      final String temporaryPath = p.join(testRoot.path, 'temporary', 'path');
+      when(mockApi.getDirectoryPath(DirectoryType.temp))
+          .thenReturn(temporaryPath);
+
       final String? path = await pathProvider.getTemporaryPath();
-      expect(
-        log,
-        <Matcher>[isMethodCall('getTemporaryDirectory', arguments: null)],
-      );
+
+      verify(mockApi.getDirectoryPath(DirectoryType.temp));
       expect(path, temporaryPath);
     });
 
     test('getApplicationSupportPath', () async {
+      final String applicationSupportPath =
+          p.join(testRoot.path, 'application', 'support', 'path');
+      when(mockApi.getDirectoryPath(DirectoryType.applicationSupport))
+          .thenReturn(applicationSupportPath);
+
       final String? path = await pathProvider.getApplicationSupportPath();
-      expect(
-        log,
-        <Matcher>[
-          isMethodCall('getApplicationSupportDirectory', arguments: null)
-        ],
-      );
+
+      verify(mockApi.getDirectoryPath(DirectoryType.applicationSupport));
       expect(path, applicationSupportPath);
     });
 
     test('getApplicationSupportPath creates the directory if necessary',
         () async {
+      final String applicationSupportPath =
+          p.join(testRoot.path, 'application', 'support', 'path');
+      when(mockApi.getDirectoryPath(DirectoryType.applicationSupport))
+          .thenReturn(applicationSupportPath);
+
       final String? path = await pathProvider.getApplicationSupportPath();
+
       expect(Directory(path!).existsSync(), isTrue);
     });
 
     test('getLibraryPath', () async {
+      final String libraryPath = p.join(testRoot.path, 'library', 'path');
+      when(mockApi.getDirectoryPath(DirectoryType.library))
+          .thenReturn(libraryPath);
+
       final String? path = await pathProvider.getLibraryPath();
-      expect(
-        log,
-        <Matcher>[isMethodCall('getLibraryDirectory', arguments: null)],
-      );
+
+      verify(mockApi.getDirectoryPath(DirectoryType.library));
       expect(path, libraryPath);
     });
 
     test('getApplicationDocumentsPath', () async {
+      final String applicationDocumentsPath =
+          p.join(testRoot.path, 'application', 'documents', 'path');
+      when(mockApi.getDirectoryPath(DirectoryType.applicationDocuments))
+          .thenReturn(applicationDocumentsPath);
+
       final String? path = await pathProvider.getApplicationDocumentsPath();
-      expect(
-        log,
-        <Matcher>[
-          isMethodCall('getApplicationDocumentsDirectory', arguments: null)
-        ],
-      );
+
+      verify(mockApi.getDirectoryPath(DirectoryType.applicationDocuments));
       expect(path, applicationDocumentsPath);
     });
 
     test('getDownloadsPath', () async {
+      final String downloadsPath = p.join(testRoot.path, 'downloads', 'path');
+      when(mockApi.getDirectoryPath(DirectoryType.downloads))
+          .thenReturn(downloadsPath);
+
       final String? result = await pathProvider.getDownloadsPath();
-      expect(
-        log,
-        <Matcher>[isMethodCall('getDownloadsDirectory', arguments: null)],
-      );
+
+      verify(mockApi.getDirectoryPath(DirectoryType.downloads));
       expect(result, downloadsPath);
     });
 
diff --git a/packages/path_provider/path_provider_macos/test/path_provider_macos_test.mocks.dart b/packages/path_provider/path_provider_macos/test/path_provider_macos_test.mocks.dart
new file mode 100644
index 0000000..eff00f7
--- /dev/null
+++ b/packages/path_provider/path_provider_macos/test/path_provider_macos_test.mocks.dart
@@ -0,0 +1,37 @@
+// Mocks generated by Mockito 5.3.2 from annotations
+// in path_provider_macos/test/path_provider_macos_test.dart.
+// Do not manually edit this file.
+
+// ignore_for_file: no_leading_underscores_for_library_prefixes
+import 'package:mockito/mockito.dart' as _i1;
+import 'package:path_provider_macos/messages.g.dart' as _i3;
+
+import 'messages_test.g.dart' as _i2;
+
+// 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: implementation_imports
+// ignore_for_file: invalid_use_of_visible_for_testing_member
+// ignore_for_file: prefer_const_constructors
+// ignore_for_file: unnecessary_parenthesis
+// ignore_for_file: camel_case_types
+// ignore_for_file: subtype_of_sealed_class
+
+/// A class which mocks [TestPathProviderApi].
+///
+/// See the documentation for Mockito's code generation for more information.
+class MockTestPathProviderApi extends _i1.Mock
+    implements _i2.TestPathProviderApi {
+  MockTestPathProviderApi() {
+    _i1.throwOnMissingStub(this);
+  }
+
+  @override
+  String? getDirectoryPath(_i3.DirectoryType? type) =>
+      (super.noSuchMethod(Invocation.method(
+        #getDirectoryPath,
+        [type],
+      )) as String?);
+}