diff --git a/README.md b/README.md index 807aa13..70fc964 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ The plugins for elinux are basically designed to be API compatible with the offi | [video_player_elinux](packages/video_player) | [video_player](https://github.com/flutter/plugins/tree/master/packages/video_player) | | [camera_elinux](packages/camera) | [camera](https://github.com/flutter/plugins/tree/master/packages/camera) | | [path_provider_elinux](packages/path_provider) | [path_provider](https://github.com/flutter/packages/tree/main/packages/path_provider) | -| [shared_preferences_elinux](packages/shared_preferences) | [shared_preferences](https://github.com/flutter/plugins/tree/master/packages/shared_preferences) | +| [shared_preferences_elinux](packages/shared_preferences) | [shared_preferences](https://github.com/flutter/packages/tree/main/packages/shared_preferences) | | [joystick](packages/joystick) | - | ## Getting Started diff --git a/packages/shared_preferences/CHANGELOG.md b/packages/shared_preferences/CHANGELOG.md index 6f20aec..2cd0e83 100644 --- a/packages/shared_preferences/CHANGELOG.md +++ b/packages/shared_preferences/CHANGELOG.md @@ -1,3 +1,6 @@ +## 2.2.0 +* Update for shared_preferences 2.2.0 + ## 1.0.1 * Update for flutter 3.3.0 release diff --git a/packages/shared_preferences/README.md b/packages/shared_preferences/README.md index 0f96b1b..4085fae 100644 --- a/packages/shared_preferences/README.md +++ b/packages/shared_preferences/README.md @@ -1,6 +1,6 @@ # shared\_preferences\_elinux -The eLinux implementation of [`shared_preferences`](https://github.com/flutter/plugins/tree/master/packages/shared_preferences). +The eLinux implementation of [`shared_preferences`](https://github.com/flutter/packages/tree/main/packages/shared_preferences/shared_preferences). ## Usage diff --git a/packages/shared_preferences/example/elinux/flutter/main.dart b/packages/shared_preferences/example/elinux/flutter/main.dart deleted file mode 100644 index fc4facb..0000000 --- a/packages/shared_preferences/example/elinux/flutter/main.dart +++ /dev/null @@ -1,12 +0,0 @@ -// -// Generated file. Do not edit. -// -// @dart=2.12 - -import 'package:shared_preferences_elinux_example/main.dart' as entrypoint; -import 'generated_plugin_registrant.dart'; - -Future main() async { - registerPlugins(); - entrypoint.main(); -} diff --git a/packages/shared_preferences/example/integration_test/shared_preferences_test.dart b/packages/shared_preferences/example/integration_test/shared_preferences_test.dart index afa761c..f8c01ff 100644 --- a/packages/shared_preferences/example/integration_test/shared_preferences_test.dart +++ b/packages/shared_preferences/example/integration_test/shared_preferences_test.dart @@ -7,94 +7,336 @@ import 'dart:async'; import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; import 'package:shared_preferences_elinux/shared_preferences_elinux.dart'; +import 'package:shared_preferences_platform_interface/shared_preferences_platform_interface.dart'; +import 'package:shared_preferences_platform_interface/types.dart'; void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); group('SharedPreferencesELinux', () { - const Map kTestValues = { - 'flutter.String': 'hello world', - 'flutter.bool': true, - 'flutter.int': 42, - 'flutter.double': 3.14159, - 'flutter.List': ['foo', 'bar'], - }; - - const Map kTestValues2 = { - 'flutter.String': 'goodbye world', - 'flutter.bool': false, - 'flutter.int': 1337, - 'flutter.double': 2.71828, - 'flutter.List': ['baz', 'quox'], - }; - late SharedPreferencesELinux preferences; + const Map flutterTestValues = { + 'flutter.String': 'hello world', + 'flutter.Bool': true, + 'flutter.Int': 42, + 'flutter.Double': 3.14159, + 'flutter.StringList': ['foo', 'bar'], + }; + + const Map prefixTestValues = { + 'prefix.String': 'hello world', + 'prefix.Bool': true, + 'prefix.Int': 42, + 'prefix.Double': 3.14159, + 'prefix.StringList': ['foo', 'bar'], + }; + + const Map nonPrefixTestValues = { + 'String': 'hello world', + 'Bool': true, + 'Int': 42, + 'Double': 3.14159, + 'StringList': ['foo', 'bar'], + }; + + final Map allTestValues = {}; + + allTestValues.addAll(flutterTestValues); + allTestValues.addAll(prefixTestValues); + allTestValues.addAll(nonPrefixTestValues); + + Future addData() async { + await preferences.setValue('String', 'String', allTestValues['String']!); + await preferences.setValue('Bool', 'Bool', allTestValues['Bool']!); + await preferences.setValue('Int', 'Int', allTestValues['Int']!); + await preferences.setValue('Double', 'Double', allTestValues['Double']!); + await preferences.setValue( + 'StringList', 'StringList', allTestValues['StringList']!); + await preferences.setValue( + 'String', 'prefix.String', allTestValues['prefix.String']!); + await preferences.setValue( + 'Bool', 'prefix.Bool', allTestValues['prefix.Bool']!); + await preferences.setValue( + 'Int', 'prefix.Int', allTestValues['prefix.Int']!); + await preferences.setValue( + 'Double', 'prefix.Double', allTestValues['prefix.Double']!); + await preferences.setValue('StringList', 'prefix.StringList', + allTestValues['prefix.StringList']!); + await preferences.setValue( + 'String', 'flutter.String', allTestValues['flutter.String']!); + await preferences.setValue( + 'Bool', 'flutter.Bool', allTestValues['flutter.Bool']!); + await preferences.setValue( + 'Int', 'flutter.Int', allTestValues['flutter.Int']!); + await preferences.setValue( + 'Double', 'flutter.Double', allTestValues['flutter.Double']!); + await preferences.setValue('StringList', 'flutter.StringList', + allTestValues['flutter.StringList']!); + } + setUp(() async { - preferences = SharedPreferencesELinux.instance; + preferences = SharedPreferencesELinux(); + await addData(); }); - tearDown(() { - preferences.clear(); + tearDown(() async { + await preferences.clearWithParameters( + ClearParameters( + filter: PreferencesFilter(prefix: ''), + ), + ); }); - testWidgets('reading', (WidgetTester _) async { - final all = await preferences.getAll(); - expect(all['String'], isNull); - expect(all['bool'], isNull); - expect(all['int'], isNull); - expect(all['double'], isNull); - expect(all['List'], isNull); + testWidgets('getAll', (WidgetTester _) async { + final Map values = await preferences.getAll(); + expect(values['flutter.String'], allTestValues['flutter.String']); + expect(values['flutter.Bool'], allTestValues['flutter.Bool']); + expect(values['flutter.Int'], allTestValues['flutter.Int']); + expect(values['flutter.Double'], allTestValues['flutter.Double']); + expect(values['flutter.StringList'], allTestValues['flutter.StringList']); }); - testWidgets('writing', (WidgetTester _) async { - await Future.wait(>[ - preferences.setValue( - 'String', 'String', kTestValues2['flutter.String']), - preferences.setValue('Bool', 'bool', kTestValues2['flutter.bool']), - preferences.setValue('Int', 'int', kTestValues2['flutter.int']), - preferences.setValue( - 'Double', 'double', kTestValues2['flutter.double']), - preferences.setValue('StringList', 'List', kTestValues2['flutter.List']) - ]); - final all = await preferences.getAll(); - expect(all['String'], kTestValues2['flutter.String']); - expect(all['bool'], kTestValues2['flutter.bool']); - expect(all['int'], kTestValues2['flutter.int']); - expect(all['double'], kTestValues2['flutter.double']); - expect(all['List'], kTestValues2['flutter.List']); + group('withPrefix', () { + testWidgets('remove', (WidgetTester _) async { + const String key = 'flutter.String'; + await preferences.remove(key); + final Map values = + await preferences.getAllWithPrefix(''); + expect(values[key], isNull); + }); + + testWidgets('clear', (WidgetTester _) async { + await preferences.clear(); + final Map values = await preferences.getAll(); + expect(values['flutter.String'], null); + expect(values['flutter.Bool'], null); + expect(values['flutter.Int'], null); + expect(values['flutter.Double'], null); + expect(values['flutter.StringList'], null); + }); + + testWidgets('get all with prefix', (WidgetTester _) async { + final Map values = + await preferences.getAllWithPrefix('prefix.'); + expect(values['prefix.String'], allTestValues['prefix.String']); + expect(values['prefix.Bool'], allTestValues['prefix.Bool']); + expect(values['prefix.Int'], allTestValues['prefix.Int']); + expect(values['prefix.Double'], allTestValues['prefix.Double']); + expect(values['prefix.StringList'], allTestValues['prefix.StringList']); + }); + + testWidgets('getAllWithNoPrefix', (WidgetTester _) async { + final Map values = + await preferences.getAllWithPrefix(''); + expect(values['String'], allTestValues['String']); + expect(values['Bool'], allTestValues['Bool']); + expect(values['Int'], allTestValues['Int']); + expect(values['Double'], allTestValues['Double']); + expect(values['StringList'], allTestValues['StringList']); + expect(values['flutter.String'], allTestValues['flutter.String']); + expect(values['flutter.Bool'], allTestValues['flutter.Bool']); + expect(values['flutter.Int'], allTestValues['flutter.Int']); + expect(values['flutter.Double'], allTestValues['flutter.Double']); + expect( + values['flutter.StringList'], allTestValues['flutter.StringList']); + }); + + testWidgets('clearWithPrefix', (WidgetTester _) async { + await preferences.clearWithPrefix('prefix.'); + Map values = + await preferences.getAllWithPrefix('prefix.'); + expect(values['prefix.String'], null); + expect(values['prefix.Bool'], null); + expect(values['prefix.Int'], null); + expect(values['prefix.Double'], null); + expect(values['prefix.StringList'], null); + values = await preferences.getAllWithPrefix('flutter.'); + expect(values['flutter.String'], allTestValues['flutter.String']); + expect(values['flutter.Bool'], allTestValues['flutter.Bool']); + expect(values['flutter.Int'], allTestValues['flutter.Int']); + expect(values['flutter.Double'], allTestValues['flutter.Double']); + expect( + values['flutter.StringList'], allTestValues['flutter.StringList']); + }); + + testWidgets('clearWithNoPrefix', (WidgetTester _) async { + await preferences.clearWithPrefix(''); + final Map values = + await preferences.getAllWithPrefix(''); + expect(values['String'], null); + expect(values['Bool'], null); + expect(values['Int'], null); + expect(values['Double'], null); + expect(values['StringList'], null); + expect(values['flutter.String'], null); + expect(values['flutter.Bool'], null); + expect(values['flutter.Int'], null); + expect(values['flutter.Double'], null); + expect(values['flutter.StringList'], null); + }); }); - testWidgets('removing', (WidgetTester _) async { - const String key = 'testKey'; + group('withParameters', () { + testWidgets('remove', (WidgetTester _) async { + const String key = 'flutter.String'; + await preferences.remove(key); + final Map values = + await preferences.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: ''), + ), + ); + expect(values[key], isNull); + }); - await Future.wait([ - preferences.setValue('String', key, kTestValues['flutter.String']), - preferences.setValue('Bool', key, kTestValues['flutter.bool']), - preferences.setValue('Int', key, kTestValues['flutter.int']), - preferences.setValue('Double', key, kTestValues['flutter.double']), - preferences.setValue('StringList', key, kTestValues['flutter.List']) - ]); - await preferences.remove(key); - final all = await preferences.getAll(); - expect(all['testKey'], isNull); - }); + testWidgets('clear', (WidgetTester _) async { + await preferences.clear(); + final Map values = await preferences.getAll(); + expect(values['flutter.String'], null); + expect(values['flutter.Bool'], null); + expect(values['flutter.Int'], null); + expect(values['flutter.Double'], null); + expect(values['flutter.StringList'], null); + }); - testWidgets('clearing', (WidgetTester _) async { - await Future.wait(>[ - preferences.setValue('String', 'String', kTestValues['flutter.String']), - preferences.setValue('Bool', 'bool', kTestValues['flutter.bool']), - preferences.setValue('Int', 'int', kTestValues['flutter.int']), - preferences.setValue('Double', 'double', kTestValues['flutter.double']), - preferences.setValue('StringList', 'List', kTestValues['flutter.List']) - ]); - await preferences.clear(); - final all = await preferences.getAll(); - expect(all['String'], null); - expect(all['bool'], null); - expect(all['int'], null); - expect(all['double'], null); - expect(all['List'], null); + testWidgets('get all with prefix', (WidgetTester _) async { + final Map values = + await preferences.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: 'prefix.'), + ), + ); + expect(values['prefix.String'], allTestValues['prefix.String']); + expect(values['prefix.Bool'], allTestValues['prefix.Bool']); + expect(values['prefix.Int'], allTestValues['prefix.Int']); + expect(values['prefix.Double'], allTestValues['prefix.Double']); + expect(values['prefix.StringList'], allTestValues['prefix.StringList']); + }); + + testWidgets('get all with allow list', (WidgetTester _) async { + final Map values = + await preferences.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter( + prefix: 'prefix.', + allowList: {'prefix.String'}, + ), + ), + ); + expect(values['prefix.String'], allTestValues['prefix.String']); + expect(values['prefix.Bool'], null); + expect(values['prefix.Int'], null); + expect(values['prefix.Double'], null); + expect(values['prefix.StringList'], null); + }); + + testWidgets('getAllWithNoPrefix', (WidgetTester _) async { + final Map values = + await preferences.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: ''), + ), + ); + expect(values['String'], allTestValues['String']); + expect(values['Bool'], allTestValues['Bool']); + expect(values['Int'], allTestValues['Int']); + expect(values['Double'], allTestValues['Double']); + expect(values['StringList'], allTestValues['StringList']); + expect(values['flutter.String'], allTestValues['flutter.String']); + expect(values['flutter.Bool'], allTestValues['flutter.Bool']); + expect(values['flutter.Int'], allTestValues['flutter.Int']); + expect(values['flutter.Double'], allTestValues['flutter.Double']); + expect( + values['flutter.StringList'], allTestValues['flutter.StringList']); + }); + + testWidgets('clearWithParameters', (WidgetTester _) async { + await preferences.clearWithParameters( + ClearParameters( + filter: PreferencesFilter(prefix: 'prefix.'), + ), + ); + Map values = await preferences.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: 'prefix.'), + ), + ); + expect(values['prefix.String'], null); + expect(values['prefix.Bool'], null); + expect(values['prefix.Int'], null); + expect(values['prefix.Double'], null); + expect(values['prefix.StringList'], null); + values = await preferences.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: 'flutter.'), + ), + ); + expect(values['flutter.String'], allTestValues['flutter.String']); + expect(values['flutter.Bool'], allTestValues['flutter.Bool']); + expect(values['flutter.Int'], allTestValues['flutter.Int']); + expect(values['flutter.Double'], allTestValues['flutter.Double']); + expect( + values['flutter.StringList'], allTestValues['flutter.StringList']); + }); + + testWidgets('clearWithParameters with allow list', + (WidgetTester _) async { + await addData(); + await preferences.clearWithParameters( + ClearParameters( + filter: PreferencesFilter( + prefix: 'prefix.', + allowList: {'prefix.StringList'}, + ), + ), + ); + Map values = await preferences.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: 'prefix.'), + ), + ); + expect(values['prefix.String'], allTestValues['prefix.String']); + expect(values['prefix.Bool'], allTestValues['prefix.Bool']); + expect(values['prefix.Int'], allTestValues['prefix.Int']); + expect(values['prefix.Double'], allTestValues['prefix.Double']); + expect(values['prefix.StringList'], null); + values = await preferences.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: 'flutter.'), + ), + ); + expect(values['flutter.String'], allTestValues['flutter.String']); + expect(values['flutter.Bool'], allTestValues['flutter.Bool']); + expect(values['flutter.Int'], allTestValues['flutter.Int']); + expect(values['flutter.Double'], allTestValues['flutter.Double']); + expect( + values['flutter.StringList'], allTestValues['flutter.StringList']); + }); + + testWidgets('clearWithNoPrefix', (WidgetTester _) async { + await preferences.clearWithParameters( + ClearParameters( + filter: PreferencesFilter(prefix: ''), + ), + ); + final Map values = + await preferences.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: ''), + ), + ); + expect(values['String'], null); + expect(values['Bool'], null); + expect(values['Int'], null); + expect(values['Double'], null); + expect(values['StringList'], null); + expect(values['flutter.String'], null); + expect(values['flutter.Bool'], null); + expect(values['flutter.Int'], null); + expect(values['flutter.Double'], null); + expect(values['flutter.StringList'], null); + }); }); }); } diff --git a/packages/shared_preferences/example/lib/main.dart b/packages/shared_preferences/example/lib/main.dart index adf99d4..657c096 100644 --- a/packages/shared_preferences/example/lib/main.dart +++ b/packages/shared_preferences/example/lib/main.dart @@ -1,4 +1,4 @@ -// Copyright 2021 Sony Group Corporation. All rights reserved. +// Copyright 2023 Sony Group Corporation. All rights reserved. // 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. @@ -11,13 +11,15 @@ import 'package:flutter/material.dart'; import 'package:shared_preferences_elinux/shared_preferences_elinux.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatelessWidget { + const MyApp({super.key}); + @override Widget build(BuildContext context) { - return MaterialApp( + return const MaterialApp( title: 'SharedPreferences Demo', home: SharedPreferencesDemo(), ); @@ -25,18 +27,18 @@ class MyApp extends StatelessWidget { } class SharedPreferencesDemo extends StatefulWidget { - SharedPreferencesDemo({Key? key}) : super(key: key); + const SharedPreferencesDemo({super.key}); @override SharedPreferencesDemoState createState() => SharedPreferencesDemoState(); } class SharedPreferencesDemoState extends State { - final prefs = SharedPreferencesELinux.instance; + final SharedPreferencesELinux prefs = SharedPreferencesELinux(); late Future _counter; Future _incrementCounter() async { - final values = await prefs.getAll(); + final Map values = await prefs.getAll(); final int counter = (values['counter'] as int? ?? 0) + 1; setState(() { @@ -50,7 +52,7 @@ class SharedPreferencesDemoState extends State { void initState() { super.initState(); _counter = prefs.getAll().then((Map values) { - return (values['counter'] as int? ?? 0); + return values['counter'] as int? ?? 0; }); } @@ -58,16 +60,18 @@ class SharedPreferencesDemoState extends State { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: const Text("SharedPreferences Demo"), + title: const Text('SharedPreferences Demo'), ), body: Center( child: FutureBuilder( future: _counter, builder: (BuildContext context, AsyncSnapshot snapshot) { switch (snapshot.connectionState) { + case ConnectionState.none: case ConnectionState.waiting: return const CircularProgressIndicator(); - default: + case ConnectionState.active: + case ConnectionState.done: if (snapshot.hasError) { return Text('Error: ${snapshot.error}'); } else { diff --git a/packages/shared_preferences/example/pubspec.yaml b/packages/shared_preferences/example/pubspec.yaml index 6dc6306..0065967 100644 --- a/packages/shared_preferences/example/pubspec.yaml +++ b/packages/shared_preferences/example/pubspec.yaml @@ -3,17 +3,18 @@ description: Demonstrates how to use the shared_preferences_elinux plugin. publish_to: none environment: - sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.10.0" + sdk: ">=2.18.0 <4.0.0" + flutter: ">=3.3.0" dependencies: flutter: sdk: flutter shared_preferences_elinux: path: ../ + shared_preferences_platform_interface: ^2.3.0 dev_dependencies: - flutter_driver: + flutter_test: sdk: flutter integration_test: sdk: flutter diff --git a/packages/shared_preferences/lib/shared_preferences_elinux.dart b/packages/shared_preferences/lib/shared_preferences_elinux.dart index 42dd141..3ae60ef 100644 --- a/packages/shared_preferences/lib/shared_preferences_elinux.dart +++ b/packages/shared_preferences/lib/shared_preferences_elinux.dart @@ -1,4 +1,4 @@ -// Copyright 2021 Sony Group Corporation. All rights reserved. +// Copyright 2023 Sony Group Corporation. All rights reserved. // 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. @@ -8,21 +8,26 @@ import 'dart:convert' show json; import 'package:file/file.dart'; import 'package:file/local.dart'; -import 'package:meta/meta.dart'; +import 'package:flutter/foundation.dart' show debugPrint, visibleForTesting; import 'package:path/path.dart' as path; import 'package:path_provider_elinux/path_provider_elinux.dart'; import 'package:shared_preferences_platform_interface/shared_preferences_platform_interface.dart'; +import 'package:shared_preferences_platform_interface/types.dart'; /// The Linux implementation of [SharedPreferencesStorePlatform]. /// /// This class implements the `package:shared_preferences` functionality for Linux. class SharedPreferencesELinux extends SharedPreferencesStorePlatform { - /// The default instance of [SharedPreferencesELinux] to use. + /// Deprecated instance of [SharedPreferencesELinux]. + /// Use [SharedPreferencesStorePlatform.instance] instead. + @Deprecated('Use `SharedPreferencesStorePlatform.instance` instead.') static SharedPreferencesELinux instance = SharedPreferencesELinux(); - /// Registers the eLinux implementation. + static const String _defaultPrefix = 'flutter.'; + + /// Registers the ELinux implementation. static void registerWith() { - SharedPreferencesStorePlatform.instance = instance; + SharedPreferencesStorePlatform.instance = SharedPreferencesELinux(); } /// Local copy of preferences @@ -30,51 +35,60 @@ class SharedPreferencesELinux extends SharedPreferencesStorePlatform { /// File system used to store to disk. Exposed for testing only. @visibleForTesting - FileSystem fs = LocalFileSystem(); + FileSystem fs = const LocalFileSystem(); + + /// The path_provider_elinux instance used to find the support directory. + @visibleForTesting + PathProviderELinux pathProvider = PathProviderELinux(); /// Gets the file where the preferences are stored. Future _getLocalDataFile() async { - final pathProvider = PathProviderELinux(); - final directory = await pathProvider.getApplicationSupportPath(); - if (directory == null) return null; + final String? directory = await pathProvider.getApplicationSupportPath(); + if (directory == null) { + return null; + } return fs.file(path.join(directory, 'shared_preferences.json')); } - /// Gets the preferences from the stored file. Once read, the preferences are - /// maintained in memory. - Future> _readPreferences() async { - if (_cachedPreferences != null) { - return _cachedPreferences!; - } - - Map preferences = {}; + /// Gets the preferences from the stored file and saves them in cache. + Future> _reload() async { + Map preferences = {}; final File? localDataFile = await _getLocalDataFile(); if (localDataFile != null && localDataFile.existsSync()) { - String stringMap = localDataFile.readAsStringSync(); + final String stringMap = localDataFile.readAsStringSync(); if (stringMap.isNotEmpty) { - preferences = json.decode(stringMap).cast(); + final Object? data = json.decode(stringMap); + if (data is Map) { + preferences = data.cast(); + } } } _cachedPreferences = preferences; return preferences; } + /// Checks for cached preferences and returns them or loads preferences from + /// file and returns and caches them. + Future> _readPreferences() async { + return _cachedPreferences ?? await _reload(); + } + /// Writes the cached preferences to disk. Returns [true] if the operation /// succeeded. Future _writePreferences(Map preferences) async { try { - var localDataFile = await _getLocalDataFile(); + final File? localDataFile = await _getLocalDataFile(); if (localDataFile == null) { - print("Unable to determine where to write preferences."); + debugPrint('Unable to determine where to write preferences.'); return false; } if (!localDataFile.existsSync()) { localDataFile.createSync(recursive: true); } - var stringMap = json.encode(preferences); + final String stringMap = json.encode(preferences); localDataFile.writeAsStringSync(stringMap); } catch (e) { - print("Error saving preferences to disk: $e"); + debugPrint('Error saving preferences to disk: $e'); return false; } return true; @@ -82,26 +96,65 @@ class SharedPreferencesELinux extends SharedPreferencesStorePlatform { @override Future clear() async { - var preferences = await _readPreferences(); - preferences.clear(); + return clearWithParameters( + ClearParameters( + filter: PreferencesFilter(prefix: _defaultPrefix), + ), + ); + } + + @override + Future clearWithPrefix(String prefix) async { + return clearWithParameters( + ClearParameters(filter: PreferencesFilter(prefix: prefix))); + } + + @override + Future clearWithParameters(ClearParameters parameters) async { + final PreferencesFilter filter = parameters.filter; + final Map preferences = await _readPreferences(); + preferences.removeWhere((String key, _) => + key.startsWith(filter.prefix) && + (filter.allowList == null || filter.allowList!.contains(key))); return _writePreferences(preferences); } @override Future> getAll() async { - return _readPreferences(); + return getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: _defaultPrefix), + ), + ); + } + + @override + Future> getAllWithPrefix(String prefix) async { + return getAllWithParameters( + GetAllParameters(filter: PreferencesFilter(prefix: prefix))); + } + + @override + Future> getAllWithParameters( + GetAllParameters parameters) async { + final PreferencesFilter filter = parameters.filter; + final Map withPrefix = + Map.from(await _readPreferences()); + withPrefix.removeWhere((String key, _) => !(key.startsWith(filter.prefix) && + (filter.allowList?.contains(key) ?? true))); + return withPrefix; } @override Future remove(String key) async { - var preferences = await _readPreferences(); + final Map preferences = await _readPreferences(); preferences.remove(key); return _writePreferences(preferences); } @override Future setValue(String valueType, String key, Object value) async { - var preferences = await _readPreferences(); + final Map preferences = await _readPreferences(); preferences[key] = value; return _writePreferences(preferences); } diff --git a/packages/shared_preferences/pubspec.yaml b/packages/shared_preferences/pubspec.yaml index bb784aa..4616db8 100644 --- a/packages/shared_preferences/pubspec.yaml +++ b/packages/shared_preferences/pubspec.yaml @@ -2,11 +2,11 @@ name: shared_preferences_elinux description: eLinux implementation of the shared_preferences plugin homepage: https://github.com/sony/flutter-elinux-plugins repository: https://github.com/sony/flutter-elinux-plugins/tree/main/packages/shared_preferences -version: 1.0.1 +version: 2.2.0 environment: - sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.10.0" + sdk: ">=2.18.0 <4.0.0" + flutter: ">=3.3.0" flutter: plugin: @@ -26,7 +26,7 @@ dependencies: url: https://github.com/sony/flutter-elinux-plugins.git path: packages/path_provider ref: main - shared_preferences_platform_interface: ^2.0.0 + shared_preferences_platform_interface: ^2.3.0 dev_dependencies: flutter_test: diff --git a/packages/shared_preferences/test/shared_preferences_elinux_test.dart b/packages/shared_preferences/test/shared_preferences_elinux_test.dart index 84bf94d..6ce8a30 100644 --- a/packages/shared_preferences/test/shared_preferences_elinux_test.dart +++ b/packages/shared_preferences/test/shared_preferences_elinux_test.dart @@ -1,84 +1,280 @@ -// Copyright 2021 Sony Group Corporation. All rights reserved. +// Copyright 2023 Sony Group Corporation. All rights reserved. // Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'dart:convert'; + import 'package:file/memory.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:path/path.dart' as path; import 'package:path_provider_elinux/path_provider_elinux.dart'; +import 'package:path_provider_platform_interface/path_provider_platform_interface.dart'; import 'package:shared_preferences_elinux/shared_preferences_elinux.dart'; import 'package:shared_preferences_platform_interface/shared_preferences_platform_interface.dart'; +import 'package:shared_preferences_platform_interface/types.dart'; void main() { late MemoryFileSystem fs; + late PathProviderELinux pathProvider; SharedPreferencesELinux.registerWith(); + const Map flutterTestValues = { + 'flutter.String': 'hello world', + 'flutter.Bool': true, + 'flutter.Int': 42, + 'flutter.Double': 3.14159, + 'flutter.StringList': ['foo', 'bar'], + }; + + const Map prefixTestValues = { + 'prefix.String': 'hello world', + 'prefix.Bool': true, + 'prefix.Int': 42, + 'prefix.Double': 3.14159, + 'prefix.StringList': ['foo', 'bar'], + }; + + const Map nonPrefixTestValues = { + 'String': 'hello world', + 'Bool': true, + 'Int': 42, + 'Double': 3.14159, + 'StringList': ['foo', 'bar'], + }; + + final Map allTestValues = {}; + + allTestValues.addAll(flutterTestValues); + allTestValues.addAll(prefixTestValues); + allTestValues.addAll(nonPrefixTestValues); + setUp(() { fs = MemoryFileSystem.test(); + pathProvider = FakePathProviderELinux(); }); - Future _getFilePath() async { - final pathProvider = PathProviderLinux(); - final directory = await pathProvider.getApplicationSupportPath(); + Future getFilePath() async { + final String? directory = await pathProvider.getApplicationSupportPath(); return path.join(directory!, 'shared_preferences.json'); } - _writeTestFile(String value) async { - fs.file(await _getFilePath()) + Future writeTestFile(String value) async { + fs.file(await getFilePath()) ..createSync(recursive: true) ..writeAsStringSync(value); } - Future _readTestFile() async { - return fs.file(await _getFilePath()).readAsStringSync(); + Future readTestFile() async { + return fs.file(await getFilePath()).readAsStringSync(); } - SharedPreferencesLinux _getPreferences() { - var prefs = SharedPreferencesLinux(); + SharedPreferencesELinux getPreferences() { + final SharedPreferencesELinux prefs = SharedPreferencesELinux(); prefs.fs = fs; + prefs.pathProvider = pathProvider; return prefs; } test('registered instance', () { + SharedPreferencesELinux.registerWith(); expect( - SharedPreferencesStorePlatform.instance, isA()); + SharedPreferencesStorePlatform.instance, isA()); }); test('getAll', () async { - await _writeTestFile('{"key1": "one", "key2": 2}'); - var prefs = _getPreferences(); + await writeTestFile(json.encode(allTestValues)); + final SharedPreferencesELinux prefs = getPreferences(); - var values = await prefs.getAll(); - expect(values, hasLength(2)); - expect(values['key1'], 'one'); - expect(values['key2'], 2); + final Map values = await prefs.getAll(); + expect(values, hasLength(5)); + expect(values, flutterTestValues); + }); + + test('getAllWithPrefix', () async { + await writeTestFile(json.encode(allTestValues)); + final SharedPreferencesELinux prefs = getPreferences(); + + final Map values = await prefs.getAllWithPrefix('prefix.'); + expect(values, hasLength(5)); + expect(values, prefixTestValues); + }); + + test('getAllWithParameters', () async { + await writeTestFile(json.encode(allTestValues)); + final SharedPreferencesELinux prefs = getPreferences(); + + final Map values = await prefs.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: 'prefix.'), + ), + ); + expect(values, hasLength(5)); + expect(values, prefixTestValues); + }); + + test('getAllWithParameters with allow list', () async { + await writeTestFile(json.encode(allTestValues)); + final SharedPreferencesELinux prefs = getPreferences(); + + final Map all = await prefs.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter( + prefix: 'prefix.', + allowList: {'prefix.Bool'}, + ), + ), + ); + expect(all.length, 1); + expect(all['prefix.Bool'], prefixTestValues['prefix.Bool']); }); test('remove', () async { - await _writeTestFile('{"key1":"one","key2":2}'); - var prefs = _getPreferences(); + await writeTestFile('{"key1":"one","key2":2}'); + final SharedPreferencesELinux prefs = getPreferences(); await prefs.remove('key2'); - expect(await _readTestFile(), '{"key1":"one"}'); + expect(await readTestFile(), '{"key1":"one"}'); }); test('setValue', () async { - await _writeTestFile('{}'); - var prefs = _getPreferences(); + await writeTestFile('{}'); + final SharedPreferencesELinux prefs = getPreferences(); await prefs.setValue('', 'key1', 'one'); await prefs.setValue('', 'key2', 2); - expect(await _readTestFile(), '{"key1":"one","key2":2}'); + expect(await readTestFile(), '{"key1":"one","key2":2}'); }); test('clear', () async { - await _writeTestFile('{"key1":"one","key2":2}'); - var prefs = _getPreferences(); + await writeTestFile(json.encode(flutterTestValues)); + final SharedPreferencesELinux prefs = getPreferences(); + expect(await readTestFile(), json.encode(flutterTestValues)); await prefs.clear(); - expect(await _readTestFile(), '{}'); + expect(await readTestFile(), '{}'); + }); + + test('clearWithPrefix', () async { + await writeTestFile(json.encode(flutterTestValues)); + final SharedPreferencesELinux prefs = getPreferences(); + await prefs.clearWithPrefix('prefix.'); + final Map noValues = + await prefs.getAllWithPrefix('prefix.'); + expect(noValues, hasLength(0)); + + final Map values = await prefs.getAll(); + expect(values, hasLength(5)); + expect(values, flutterTestValues); + }); + + test('getAllWithNoPrefix', () async { + await writeTestFile(json.encode(allTestValues)); + final SharedPreferencesELinux prefs = getPreferences(); + + final Map values = await prefs.getAllWithPrefix(''); + expect(values, hasLength(15)); + expect(values, allTestValues); + }); + + test('clearWithNoPrefix', () async { + await writeTestFile(json.encode(flutterTestValues)); + final SharedPreferencesELinux prefs = getPreferences(); + await prefs.clearWithPrefix(''); + final Map noValues = await prefs.getAllWithPrefix(''); + expect(noValues, hasLength(0)); + }); + + test('clearWithParameters', () async { + await writeTestFile(json.encode(flutterTestValues)); + final SharedPreferencesELinux prefs = getPreferences(); + await prefs.clearWithParameters( + ClearParameters( + filter: PreferencesFilter(prefix: 'prefix.'), + ), + ); + final Map noValues = await prefs.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: 'prefix.'), + ), + ); + expect(noValues, hasLength(0)); + + final Map values = await prefs.getAll(); + expect(values, hasLength(5)); + expect(values, flutterTestValues); + }); + + test('clearWithParameters with allow list', () async { + await writeTestFile(json.encode(prefixTestValues)); + final SharedPreferencesELinux prefs = getPreferences(); + await prefs.clearWithParameters( + ClearParameters( + filter: PreferencesFilter( + prefix: 'prefix.', + allowList: {'prefix.StringList'}, + ), + ), + ); + final Map someValues = await prefs.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: 'prefix.'), + ), + ); + expect(someValues, hasLength(4)); + }); + + test('getAllWithNoPrefix', () async { + await writeTestFile(json.encode(allTestValues)); + final SharedPreferencesELinux prefs = getPreferences(); + + final Map values = await prefs.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: ''), + ), + ); + expect(values, hasLength(15)); + expect(values, allTestValues); + }); + + test('clearWithNoPrefix', () async { + await writeTestFile(json.encode(flutterTestValues)); + final SharedPreferencesELinux prefs = getPreferences(); + await prefs.clearWithParameters( + ClearParameters( + filter: PreferencesFilter(prefix: ''), + ), + ); + final Map noValues = await prefs.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: ''), + ), + ); + expect(noValues, hasLength(0)); }); } + +/// Fake implementation of PathProviderELinux that returns hard-coded paths, +/// allowing tests to run on any platform. +/// +/// Note that this should only be used with an in-memory filesystem, as the +/// path it returns is a root path that does not actually exist on Linux. +class FakePathProviderELinux extends PathProviderPlatform + implements PathProviderELinux { + @override + Future getApplicationSupportPath() async => r'/appsupport'; + + @override + Future getTemporaryPath() async => null; + + @override + Future getLibraryPath() async => null; + + @override + Future getApplicationDocumentsPath() async => null; + + @override + Future getDownloadsPath() async => null; +}