diff --git a/packages/shared_preferences/shared_preferences_macos/CHANGELOG.md b/packages/shared_preferences/shared_preferences_macos/CHANGELOG.md index fc8a78af95b9..f0a852637c52 100644 --- a/packages/shared_preferences/shared_preferences_macos/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_macos/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 2.0.5 +* Converts platform channel to Pigeon. * Updates code for `no_leading_underscores_for_local_identifiers` lint. * Updates minimum Flutter version to 2.10. diff --git a/packages/shared_preferences/shared_preferences_macos/example/macos/RunnerTests/RunnerTests.swift b/packages/shared_preferences/shared_preferences_macos/example/macos/RunnerTests/RunnerTests.swift index 7da66cbc80df..9a1b2f0e717f 100644 --- a/packages/shared_preferences/shared_preferences_macos/example/macos/RunnerTests/RunnerTests.swift +++ b/packages/shared_preferences/shared_preferences_macos/example/macos/RunnerTests/RunnerTests.swift @@ -4,85 +4,55 @@ import FlutterMacOS import XCTest -import shared_preferences_macos +@testable import shared_preferences_macos class RunnerTests: XCTestCase { - func testHandlesCommitNoOp() throws { + func testSetAndGet() throws { let plugin = SharedPreferencesPlugin() - let call = FlutterMethodCall(methodName: "commit", arguments: nil) - var called = false - plugin.handle( - call, - result: { (result: Any?) -> Void in - called = true - XCTAssert(result as? Bool == true) - }) - XCTAssert(called) + + plugin.setBool(key: "flutter.aBool", value: true) + plugin.setDouble(key: "flutter.aDouble", value: 3.14) + plugin.setValue(key: "flutter.anInt", value: 42) + plugin.setValue(key: "flutter.aString", value: "hello world") + plugin.setValue(key: "flutter.aStringList", value: ["hello", "world"]) + + let storedValues = plugin.getAll() + XCTAssertEqual(storedValues["flutter.aBool"] as? Bool, true) + XCTAssertEqual(storedValues["flutter.aDouble"] as! Double, 3.14, accuracy: 0.0001) + XCTAssertEqual(storedValues["flutter.anInt"] as? Int, 42) + XCTAssertEqual(storedValues["flutter.aString"] as? String, "hello world") + XCTAssertEqual(storedValues["flutter.aStringList"] as? Array, ["hello", "world"]) } - func testSetAndGet() throws { + func testRemove() throws { let plugin = SharedPreferencesPlugin() - let setCall = FlutterMethodCall( - methodName: "setInt", - arguments: [ - "key": "flutter.foo", - "value": 42, - ]) - plugin.handle( - setCall, - result: { (result: Any?) -> Void in - XCTAssert(result as? Bool == true) - }) + let testKey = "flutter.foo" + plugin.setValue(key: testKey, value: 42) + + // Make sure there is something to remove, so the test can't pass due to a set failure. + let preRemovalValues = plugin.getAll() + XCTAssertEqual(preRemovalValues[testKey] as? Int, 42) + + // Then verify that removing it works. + plugin.remove(key: testKey) - var value: Int? - plugin.handle( - FlutterMethodCall(methodName: "getAll", arguments: nil), - result: { (result: Any?) -> Void in - if let prefs = result as? [String: Any] { - value = prefs["flutter.foo"] as? Int - } - }) - XCTAssertEqual(value, 42) + let finalValues = plugin.getAll() + XCTAssertNil(finalValues[testKey] as Any?) } func testClear() throws { let plugin = SharedPreferencesPlugin() - let setCall = FlutterMethodCall( - methodName: "setInt", - arguments: [ - "key": "flutter.foo", - "value": 42, - ]) - plugin.handle(setCall, result: { (result: Any?) -> Void in }) + let testKey = "flutter.foo" + plugin.setValue(key: testKey, value: 42) // Make sure there is something to clear, so the test can't pass due to a set failure. - let getCall = FlutterMethodCall(methodName: "getAll", arguments: nil) - var value: Int? - plugin.handle( - getCall, - result: { (result: Any?) -> Void in - if let prefs = result as? [String: Any] { - value = prefs["flutter.foo"] as? Int - } - }) - XCTAssertEqual(value, 42) + let preRemovalValues = plugin.getAll() + XCTAssertEqual(preRemovalValues[testKey] as? Int, 42) - // Clear the value. - plugin.handle( - FlutterMethodCall(methodName: "clear", arguments: nil), - result: { (result: Any?) -> Void in - XCTAssert(result as? Bool == true) - }) + // Then verify that clearing works. + plugin.clear() - // Get the value again, which should clear |value|. - plugin.handle( - getCall, - result: { (result: Any?) -> Void in - if let prefs = result as? [String: Any] { - value = prefs["flutter.foo"] as? Int - XCTAssert(prefs.isEmpty) - } - }) - XCTAssertEqual(value, nil) + let finalValues = plugin.getAll() + XCTAssertNil(finalValues[testKey] as Any?) } } diff --git a/packages/shared_preferences/shared_preferences_macos/lib/messages.g.dart b/packages/shared_preferences/shared_preferences_macos/lib/messages.g.dart new file mode 100644 index 000000000000..f7c6c21567d2 --- /dev/null +++ b/packages/shared_preferences/shared_preferences_macos/lib/messages.g.dart @@ -0,0 +1,157 @@ +// 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'; + +class UserDefaultsApi { + /// Constructor for [UserDefaultsApi]. 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. + UserDefaultsApi({BinaryMessenger? binaryMessenger}) + : _binaryMessenger = binaryMessenger; + final BinaryMessenger? _binaryMessenger; + + static const MessageCodec codec = StandardMessageCodec(); + + Future remove(String arg_key) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.UserDefaultsApi.remove', codec, + binaryMessenger: _binaryMessenger); + final List? replyList = + await channel.send([arg_key]) as List?; + 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; + } + } + + Future setBool(String arg_key, bool arg_value) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.UserDefaultsApi.setBool', codec, + binaryMessenger: _binaryMessenger); + final List? replyList = + await channel.send([arg_key, arg_value]) as List?; + 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; + } + } + + Future setDouble(String arg_key, double arg_value) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.UserDefaultsApi.setDouble', codec, + binaryMessenger: _binaryMessenger); + final List? replyList = + await channel.send([arg_key, arg_value]) as List?; + 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; + } + } + + Future setValue(String arg_key, Object arg_value) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.UserDefaultsApi.setValue', codec, + binaryMessenger: _binaryMessenger); + final List? replyList = + await channel.send([arg_key, arg_value]) as List?; + 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; + } + } + + Future> getAll() async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.UserDefaultsApi.getAll', codec, + binaryMessenger: _binaryMessenger); + final List? replyList = await channel.send(null) as List?; + 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 if (replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (replyList[0] as Map?)!.cast(); + } + } + + Future clear() async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.UserDefaultsApi.clear', codec, + binaryMessenger: _binaryMessenger); + final List? replyList = await channel.send(null) as List?; + 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; + } + } +} diff --git a/packages/shared_preferences/shared_preferences_macos/lib/shared_preferences_macos.dart b/packages/shared_preferences/shared_preferences_macos/lib/shared_preferences_macos.dart index a97fe131af5c..25e8c5d3f50c 100644 --- a/packages/shared_preferences/shared_preferences_macos/lib/shared_preferences_macos.dart +++ b/packages/shared_preferences/shared_preferences_macos/lib/shared_preferences_macos.dart @@ -2,52 +2,66 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:async'; - import 'package:flutter/services.dart'; import 'package:shared_preferences_platform_interface/shared_preferences_platform_interface.dart'; +import 'messages.g.dart'; -const MethodChannel _kChannel = - MethodChannel('plugins.flutter.io/shared_preferences_macos'); +typedef _Setter = Future Function(String key, Object value); -/// The macOS implementation of [SharedPreferencesStorePlatform]. -/// -/// This class implements the `package:shared_preferences` functionality for macOS. +/// macOS implementation of shared_preferences. class SharedPreferencesMacOS extends SharedPreferencesStorePlatform { - /// Registers this class as the default instance of [SharedPreferencesStorePlatform]. + final UserDefaultsApi _api = UserDefaultsApi(); + late final Map _setters = { + 'Bool': (String key, Object value) { + return _api.setBool(key, value as bool); + }, + 'Double': (String key, Object value) { + return _api.setDouble(key, value as double); + }, + 'Int': (String key, Object value) { + return _api.setValue(key, value as int); + }, + 'String': (String key, Object value) { + return _api.setValue(key, value as String); + }, + 'StringList': (String key, Object value) { + return _api.setValue(key, value as List); + }, + }; + + /// Registers this class as the default instance of + /// [SharedPreferencesStorePlatform]. static void registerWith() { SharedPreferencesStorePlatform.instance = SharedPreferencesMacOS(); } @override - Future remove(String key) async { - return (await _kChannel.invokeMethod( - 'remove', - {'key': key}, - ))!; + Future clear() async { + await _api.clear(); + return true; } @override - Future setValue(String valueType, String key, Object value) async { - return (await _kChannel.invokeMethod( - 'set$valueType', - {'key': key, 'value': value}, - ))!; + Future> getAll() async { + final Map result = await _api.getAll(); + return result.cast(); } @override - Future clear() async { - return (await _kChannel.invokeMethod('clear'))!; + Future remove(String key) async { + await _api.remove(key); + return true; } @override - Future> getAll() async { - final Map? preferences = - await _kChannel.invokeMapMethod('getAll'); - - if (preferences == null) { - return {}; + Future setValue(String valueType, String key, Object value) async { + final _Setter? setter = _setters[valueType]; + if (setter == null) { + throw PlatformException( + code: 'InvalidOperation', + message: '"$valueType" is not a supported type.'); } - return preferences; + await setter(key, value); + return true; } } diff --git a/packages/shared_preferences/shared_preferences_macos/macos/Classes/SharedPreferencesPlugin.swift b/packages/shared_preferences/shared_preferences_macos/macos/Classes/SharedPreferencesPlugin.swift index 91b42441adda..6e5e79d53a35 100644 --- a/packages/shared_preferences/shared_preferences_macos/macos/Classes/SharedPreferencesPlugin.swift +++ b/packages/shared_preferences/shared_preferences_macos/macos/Classes/SharedPreferencesPlugin.swift @@ -5,44 +5,36 @@ import FlutterMacOS import Foundation -public class SharedPreferencesPlugin: NSObject, FlutterPlugin { +public class SharedPreferencesPlugin: NSObject, FlutterPlugin, UserDefaultsApi { public static func register(with registrar: FlutterPluginRegistrar) { - let channel = FlutterMethodChannel( - name: "plugins.flutter.io/shared_preferences_macos", - binaryMessenger: registrar.messenger) let instance = SharedPreferencesPlugin() - registrar.addMethodCallDelegate(instance, channel: channel) - } - - public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { - switch call.method { - case "getAll": - result(getAllPrefs()) - case "setBool", - "setInt", - "setDouble", - "setString", - "setStringList": - let arguments = call.arguments as! [String: Any] - let key = arguments["key"] as! String - UserDefaults.standard.set(arguments["value"], forKey: key) - result(true) - case "commit": - // UserDefaults does not need to be synchronized. - result(true) - case "remove": - let arguments = call.arguments as! [String: Any] - let key = arguments["key"] as! String - UserDefaults.standard.removeObject(forKey: key) - result(true) - case "clear": - let defaults = UserDefaults.standard - for (key, _) in getAllPrefs() { - defaults.removeObject(forKey: key) - } - result(true) - default: - result(FlutterMethodNotImplemented) + UserDefaultsApiSetup.setUp(binaryMessenger: registrar.messenger, api: instance) + } + + func getAll() -> [String? : Any?] { + return getAllPrefs(); + } + + func setBool(key: String, value: Bool) { + UserDefaults.standard.set(value, forKey: key) + } + + func setDouble(key: String, value: Double) { + UserDefaults.standard.set(value, forKey: key) + } + + func setValue(key: String, value: Any) { + UserDefaults.standard.set(value, forKey: key) + } + + func remove(key: String) { + UserDefaults.standard.removeObject(forKey: key) + } + + func clear() { + let defaults = UserDefaults.standard + for (key, _) in getAllPrefs() { + defaults.removeObject(forKey: key) } } } diff --git a/packages/shared_preferences/shared_preferences_macos/macos/Classes/messages.g.swift b/packages/shared_preferences/shared_preferences_macos/macos/Classes/messages.g.swift new file mode 100644 index 000000000000..933217b7bf96 --- /dev/null +++ b/packages/shared_preferences/shared_preferences_macos/macos/Classes/messages.g.swift @@ -0,0 +1,111 @@ +// 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. +/// Generated protocol from Pigeon that represents a handler of messages from Flutter. +protocol UserDefaultsApi { + func remove(key: String) + func setBool(key: String, value: Bool) + func setDouble(key: String, value: Double) + func setValue(key: String, value: Any) + func getAll() -> [String?: Any?] + func clear() +} + +/// Generated setup class from Pigeon to handle messages through the `binaryMessenger`. +class UserDefaultsApiSetup { + /// The codec used by UserDefaultsApi. + /// Sets up an instance of `UserDefaultsApi` to handle messages through the `binaryMessenger`. + static func setUp(binaryMessenger: FlutterBinaryMessenger, api: UserDefaultsApi?) { + let removeChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.UserDefaultsApi.remove", binaryMessenger: binaryMessenger) + if let api = api { + removeChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let keyArg = args[0] as! String + api.remove(key: keyArg) + reply(wrapResult(nil)) + } + } else { + removeChannel.setMessageHandler(nil) + } + let setBoolChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.UserDefaultsApi.setBool", binaryMessenger: binaryMessenger) + if let api = api { + setBoolChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let keyArg = args[0] as! String + let valueArg = args[1] as! Bool + api.setBool(key: keyArg, value: valueArg) + reply(wrapResult(nil)) + } + } else { + setBoolChannel.setMessageHandler(nil) + } + let setDoubleChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.UserDefaultsApi.setDouble", binaryMessenger: binaryMessenger) + if let api = api { + setDoubleChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let keyArg = args[0] as! String + let valueArg = args[1] as! Double + api.setDouble(key: keyArg, value: valueArg) + reply(wrapResult(nil)) + } + } else { + setDoubleChannel.setMessageHandler(nil) + } + let setValueChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.UserDefaultsApi.setValue", binaryMessenger: binaryMessenger) + if let api = api { + setValueChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let keyArg = args[0] as! String + let valueArg = args[1]! + api.setValue(key: keyArg, value: valueArg) + reply(wrapResult(nil)) + } + } else { + setValueChannel.setMessageHandler(nil) + } + let getAllChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.UserDefaultsApi.getAll", binaryMessenger: binaryMessenger) + if let api = api { + getAllChannel.setMessageHandler { _, reply in + let result = api.getAll() + reply(wrapResult(result)) + } + } else { + getAllChannel.setMessageHandler(nil) + } + let clearChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.UserDefaultsApi.clear", binaryMessenger: binaryMessenger) + if let api = api { + clearChannel.setMessageHandler { _, reply in + api.clear() + reply(wrapResult(nil)) + } + } else { + clearChannel.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/shared_preferences/shared_preferences_macos/pigeons/copyright_header.txt b/packages/shared_preferences/shared_preferences_macos/pigeons/copyright_header.txt new file mode 100644 index 000000000000..fb682b1ab965 --- /dev/null +++ b/packages/shared_preferences/shared_preferences_macos/pigeons/copyright_header.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. \ No newline at end of file diff --git a/packages/shared_preferences/shared_preferences_macos/pigeons/messages.dart b/packages/shared_preferences/shared_preferences_macos/pigeons/messages.dart new file mode 100644 index 000000000000..bda7185fefb5 --- /dev/null +++ b/packages/shared_preferences/shared_preferences_macos/pigeons/messages.dart @@ -0,0 +1,25 @@ +// 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( + dartOut: 'lib/messages.g.dart', + dartTestOut: 'test/test_api.g.dart', + swiftOut: 'macos/Classes/messages.g.swift', + copyrightHeader: 'pigeons/copyright_header.txt', +)) +@HostApi(dartHostTestHandler: 'TestUserDefaultsApi') +abstract class UserDefaultsApi { + void remove(String key); + // TODO(stuartmorgan): Give these setters better Swift signatures (_,forKey:) + // once https://github.com/flutter/flutter/issues/105932 is fixed. + void setBool(String key, bool value); + void setDouble(String key, double value); + void setValue(String key, Object value); + // TODO(stuartmorgan): Make these non-nullable once + // https://github.com/flutter/flutter/issues/97848 is fixed. + Map getAll(); + void clear(); +} diff --git a/packages/shared_preferences/shared_preferences_macos/pubspec.yaml b/packages/shared_preferences/shared_preferences_macos/pubspec.yaml index 77f5f11f0525..2ddf99816d2f 100644 --- a/packages/shared_preferences/shared_preferences_macos/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_macos/pubspec.yaml @@ -2,7 +2,7 @@ name: shared_preferences_macos description: macOS implementation of the shared_preferences plugin. repository: https://github.com/flutter/plugins/tree/main/packages/shared_preferences/shared_preferences_macos issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 -version: 2.0.4 +version: 2.0.5 environment: sdk: ">=2.12.0 <3.0.0" @@ -24,3 +24,4 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter + pigeon: ^5.0.0 diff --git a/packages/shared_preferences/shared_preferences_macos/test/shared_preferences_macos_test.dart b/packages/shared_preferences/shared_preferences_macos/test/shared_preferences_macos_test.dart index 8e71e403ca03..f32ba4c77915 100644 --- a/packages/shared_preferences/shared_preferences_macos/test/shared_preferences_macos_test.dart +++ b/packages/shared_preferences/shared_preferences_macos/test/shared_preferences_macos_test.dart @@ -5,113 +5,111 @@ import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:shared_preferences_macos/shared_preferences_macos.dart'; -import 'package:shared_preferences_platform_interface/method_channel_shared_preferences.dart'; import 'package:shared_preferences_platform_interface/shared_preferences_platform_interface.dart'; +import 'test_api.g.dart'; + +class _MockSharedPreferencesApi implements TestUserDefaultsApi { + final Map items = {}; + + @override + Map getAll() { + return items; + } + + @override + void remove(String key) { + items.remove(key); + } + + @override + void setBool(String key, bool value) { + items[key] = value; + } + + @override + void setDouble(String key, double value) { + items[key] = value; + } + + @override + void setValue(String key, Object value) { + items[key] = value; + } + + @override + void clear() { + items.clear(); + } +} + void main() { TestWidgetsFlutterBinding.ensureInitialized(); + late _MockSharedPreferencesApi api; + + setUp(() { + api = _MockSharedPreferencesApi(); + TestUserDefaultsApi.setup(api); + }); + + test('registerWith', () { + SharedPreferencesMacOS.registerWith(); + expect( + SharedPreferencesStorePlatform.instance, isA()); + }); + + test('remove', () async { + final SharedPreferencesMacOS plugin = SharedPreferencesMacOS(); + api.items['flutter.hi'] = 'world'; + expect(await plugin.remove('flutter.hi'), isTrue); + expect(api.items.containsKey('flutter.hi'), isFalse); + }); + + test('clear', () async { + final SharedPreferencesMacOS plugin = SharedPreferencesMacOS(); + api.items['flutter.hi'] = 'world'; + expect(await plugin.clear(), isTrue); + expect(api.items.containsKey('flutter.hi'), isFalse); + }); + + test('getAll', () async { + final SharedPreferencesMacOS plugin = SharedPreferencesMacOS(); + api.items['flutter.aBool'] = true; + api.items['flutter.aDouble'] = 3.14; + api.items['flutter.anInt'] = 42; + api.items['flutter.aString'] = 'hello world'; + api.items['flutter.aStringList'] = ['hello', 'world']; + final Map all = await plugin.getAll(); + expect(all.length, 5); + expect(all['flutter.aBool'], api.items['flutter.aBool']); + expect(all['flutter.aDouble'], + closeTo(api.items['flutter.aDouble']! as num, 0.0001)); + expect(all['flutter.anInt'], api.items['flutter.anInt']); + expect(all['flutter.aString'], api.items['flutter.aString']); + expect(all['flutter.aStringList'], api.items['flutter.aStringList']); + }); + + test('setValue', () async { + final SharedPreferencesMacOS plugin = SharedPreferencesMacOS(); + expect(await plugin.setValue('Bool', 'flutter.Bool', true), isTrue); + expect(api.items['flutter.Bool'], true); + expect(await plugin.setValue('Double', 'flutter.Double', 1.5), isTrue); + expect(api.items['flutter.Double'], 1.5); + expect(await plugin.setValue('Int', 'flutter.Int', 12), isTrue); + expect(api.items['flutter.Int'], 12); + expect(await plugin.setValue('String', 'flutter.String', 'hi'), isTrue); + expect(api.items['flutter.String'], 'hi'); + expect( + await plugin + .setValue('StringList', 'flutter.StringList', ['hi']), + isTrue); + expect(api.items['flutter.StringList'], ['hi']); + }); - group(MethodChannelSharedPreferencesStore, () { - const MethodChannel channel = MethodChannel( - 'plugins.flutter.io/shared_preferences_macos', - ); - - const Map kTestValues = { - 'flutter.String': 'hello world', - 'flutter.Bool': true, - 'flutter.Int': 42, - 'flutter.Double': 3.14159, - 'flutter.StringList': ['foo', 'bar'], - }; - // Create a dummy in-memory implementation to back the mocked method channel - // API to simplify validation of the expected calls. - late InMemorySharedPreferencesStore testData; - - final List log = []; - late SharedPreferencesStorePlatform store; - - setUp(() async { - testData = InMemorySharedPreferencesStore.empty(); - - channel.setMockMethodCallHandler((MethodCall methodCall) async { - log.add(methodCall); - if (methodCall.method == 'getAll') { - return testData.getAll(); - } - if (methodCall.method == 'remove') { - final String key = (methodCall.arguments['key'] as String?)!; - return testData.remove(key); - } - if (methodCall.method == 'clear') { - return testData.clear(); - } - final RegExp setterRegExp = RegExp(r'set(.*)'); - final Match? match = setterRegExp.matchAsPrefix(methodCall.method); - if (match?.groupCount == 1) { - final String valueType = match!.group(1)!; - final String key = (methodCall.arguments['key'] as String?)!; - final Object value = (methodCall.arguments['value'] as Object?)!; - return testData.setValue(valueType, key, value); - } - fail('Unexpected method call: ${methodCall.method}'); - }); - log.clear(); - }); - - test('registers instance', () { - SharedPreferencesMacOS.registerWith(); - expect(SharedPreferencesStorePlatform.instance, - isA()); - }); - - test('getAll', () async { - store = SharedPreferencesMacOS(); - testData = InMemorySharedPreferencesStore.withData(kTestValues); - expect(await store.getAll(), kTestValues); - expect(log.single.method, 'getAll'); - }); - - test('remove', () async { - store = SharedPreferencesMacOS(); - testData = InMemorySharedPreferencesStore.withData(kTestValues); - expect(await store.remove('flutter.String'), true); - expect(await store.remove('flutter.Bool'), true); - expect(await store.remove('flutter.Int'), true); - expect(await store.remove('flutter.Double'), true); - expect(await testData.getAll(), { - 'flutter.StringList': ['foo', 'bar'], - }); - - expect(log, hasLength(4)); - for (final MethodCall call in log) { - expect(call.method, 'remove'); - } - }); - - test('setValue', () async { - store = SharedPreferencesMacOS(); - expect(await testData.getAll(), isEmpty); - for (final String key in kTestValues.keys) { - final Object value = kTestValues[key]!; - expect(await store.setValue(key.split('.').last, key, value), true); - } - expect(await testData.getAll(), kTestValues); - - expect(log, hasLength(5)); - expect(log[0].method, 'setString'); - expect(log[1].method, 'setBool'); - expect(log[2].method, 'setInt'); - expect(log[3].method, 'setDouble'); - expect(log[4].method, 'setStringList'); - }); - - test('clear', () async { - store = SharedPreferencesMacOS(); - testData = InMemorySharedPreferencesStore.withData(kTestValues); - expect(await testData.getAll(), isNotEmpty); - expect(await store.clear(), true); - expect(await testData.getAll(), isEmpty); - expect(log.single.method, 'clear'); - }); + test('setValue with unsupported type', () { + final SharedPreferencesMacOS plugin = SharedPreferencesMacOS(); + expect(() async { + await plugin.setValue('Map', 'flutter.key', {}); + }, throwsA(isA())); }); } diff --git a/packages/shared_preferences/shared_preferences_macos/test/test_api.g.dart b/packages/shared_preferences/shared_preferences_macos/test/test_api.g.dart new file mode 100644 index 000000000000..baa67374932f --- /dev/null +++ b/packages/shared_preferences/shared_preferences_macos/test/test_api.g.dart @@ -0,0 +1,147 @@ +// 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:shared_preferences_macos/messages.g.dart'; + +abstract class TestUserDefaultsApi { + static const MessageCodec codec = StandardMessageCodec(); + + void remove(String key); + + void setBool(String key, bool value); + + void setDouble(String key, double value); + + void setValue(String key, Object value); + + Map getAll(); + + void clear(); + + static void setup(TestUserDefaultsApi? api, + {BinaryMessenger? binaryMessenger}) { + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.UserDefaultsApi.remove', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.UserDefaultsApi.remove was null.'); + final List args = (message as List?)!; + final String? arg_key = (args[0] as String?); + assert(arg_key != null, + 'Argument for dev.flutter.pigeon.UserDefaultsApi.remove was null, expected non-null String.'); + api.remove(arg_key!); + return []; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.UserDefaultsApi.setBool', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.UserDefaultsApi.setBool was null.'); + final List args = (message as List?)!; + final String? arg_key = (args[0] as String?); + assert(arg_key != null, + 'Argument for dev.flutter.pigeon.UserDefaultsApi.setBool was null, expected non-null String.'); + final bool? arg_value = (args[1] as bool?); + assert(arg_value != null, + 'Argument for dev.flutter.pigeon.UserDefaultsApi.setBool was null, expected non-null bool.'); + api.setBool(arg_key!, arg_value!); + return []; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.UserDefaultsApi.setDouble', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.UserDefaultsApi.setDouble was null.'); + final List args = (message as List?)!; + final String? arg_key = (args[0] as String?); + assert(arg_key != null, + 'Argument for dev.flutter.pigeon.UserDefaultsApi.setDouble was null, expected non-null String.'); + final double? arg_value = (args[1] as double?); + assert(arg_value != null, + 'Argument for dev.flutter.pigeon.UserDefaultsApi.setDouble was null, expected non-null double.'); + api.setDouble(arg_key!, arg_value!); + return []; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.UserDefaultsApi.setValue', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.UserDefaultsApi.setValue was null.'); + final List args = (message as List?)!; + final String? arg_key = (args[0] as String?); + assert(arg_key != null, + 'Argument for dev.flutter.pigeon.UserDefaultsApi.setValue was null, expected non-null String.'); + final Object? arg_value = (args[1] as Object?); + assert(arg_value != null, + 'Argument for dev.flutter.pigeon.UserDefaultsApi.setValue was null, expected non-null Object.'); + api.setValue(arg_key!, arg_value!); + return []; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.UserDefaultsApi.getAll', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + // ignore message + final Map output = api.getAll(); + return [output]; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.UserDefaultsApi.clear', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + // ignore message + api.clear(); + return []; + }); + } + } + } +}