From 6318eea894b31a09d95b675c9e910bdeb0a3f624 Mon Sep 17 00:00:00 2001 From: Swanseo0 <98388769+Swanseo0@users.noreply.github.com> Date: Fri, 1 Sep 2023 10:14:53 +0900 Subject: [PATCH 1/2] [flutter_secure_storage] Update flutter_secure_storage to 9.0.0 (#613) --- packages/flutter_secure_storage/CHANGELOG.md | 4 +- packages/flutter_secure_storage/README.md | 4 +- .../analysis_options.yaml | 2 +- .../example/lib/main.dart | 54 +++++++++++++++++-- .../example/pubspec.yaml | 2 +- packages/flutter_secure_storage/pubspec.yaml | 4 +- 6 files changed, 60 insertions(+), 10 deletions(-) diff --git a/packages/flutter_secure_storage/CHANGELOG.md b/packages/flutter_secure_storage/CHANGELOG.md index 2f66a9f25..c8447bb42 100644 --- a/packages/flutter_secure_storage/CHANGELOG.md +++ b/packages/flutter_secure_storage/CHANGELOG.md @@ -1,5 +1,7 @@ -## NEXT +## 0.1.1 +* Update flutter_secure_storage to 9.0.0. +* Update example app. * Increase the minimum Flutter version to 3.3. ## 0.1.0 diff --git a/packages/flutter_secure_storage/README.md b/packages/flutter_secure_storage/README.md index 20fc95650..42dc3119c 100644 --- a/packages/flutter_secure_storage/README.md +++ b/packages/flutter_secure_storage/README.md @@ -10,8 +10,8 @@ This package is not an _endorsed_ implementation of `flutter_secure_storage`. Th ```yaml dependencies: - flutter_secure_storage: ^6.0.0 - flutter_secure_storage_tizen: ^0.1.0 + flutter_secure_storage: ^9.0.0 + flutter_secure_storage_tizen: ^0.1.1 ``` Then you can import `flutter_secure_storage` in your Dart code: diff --git a/packages/flutter_secure_storage/analysis_options.yaml b/packages/flutter_secure_storage/analysis_options.yaml index b95969e8f..a86ab610e 100644 --- a/packages/flutter_secure_storage/analysis_options.yaml +++ b/packages/flutter_secure_storage/analysis_options.yaml @@ -7,7 +7,7 @@ # The following line activates a set of recommended lints for Flutter apps, # packages, and plugins designed to encourage good coding practices. -include: package:lint/analysis_options_package.yaml +include: package:lint/package.yaml linter: # The lint rules applied to this project can be customized in the diff --git a/packages/flutter_secure_storage/example/lib/main.dart b/packages/flutter_secure_storage/example/lib/main.dart index e4d7c5785..96f07352a 100644 --- a/packages/flutter_secure_storage/example/lib/main.dart +++ b/packages/flutter_secure_storage/example/lib/main.dart @@ -19,7 +19,7 @@ class ItemsWidget extends StatefulWidget { enum _Actions { deleteAll } -enum _ItemActions { delete, edit, containsKey } +enum _ItemActions { delete, edit, containsKey, read } class ItemsWidgetState extends State { final _storage = const FlutterSecureStorage(); @@ -153,6 +153,13 @@ class ItemsWidgetState extends State { key: Key('contains_row_$index'), ), ), + PopupMenuItem( + value: _ItemActions.read, + child: Text( + 'Read', + key: Key('contains_row_$index'), + ), + ), ], ), title: Text( @@ -202,17 +209,58 @@ class ItemsWidgetState extends State { } break; case _ItemActions.containsKey: - final result = await _storage.containsKey(key: item.key); + if (!context.mounted) return; + final key = await _displayTextInputDialog(context, item.key); + final result = await _storage.containsKey(key: key); + if (!mounted) return; + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text('Contains Key: $result, key checked: $key'), + backgroundColor: result ? Colors.green : Colors.red, + ), + ); + break; + case _ItemActions.read: + if (!context.mounted) return; + final key = await _displayTextInputDialog(context, item.key); + final result = + await _storage.read(key: key, aOptions: _getAndroidOptions()); if (!mounted) return; ScaffoldMessenger.of(context).showSnackBar( SnackBar( - content: Text('Contains Key: $result'), + content: Text('value: $result'), ), ); break; } } + Future _displayTextInputDialog( + BuildContext context, + String key, + ) async { + final controller = TextEditingController(); + controller.text = key; + await showDialog( + context: context, + builder: (context) { + return AlertDialog( + title: const Text('Check if key exists'), + actions: [ + TextButton( + onPressed: () => Navigator.of(context).pop(), + child: const Text('OK'), + ), + ], + content: TextField( + controller: controller, + ), + ); + }, + ); + return controller.text; + } + String _randomValue() { final rand = Random(); final codeUnits = List.generate(20, (index) { diff --git a/packages/flutter_secure_storage/example/pubspec.yaml b/packages/flutter_secure_storage/example/pubspec.yaml index 05b78c38c..172888309 100644 --- a/packages/flutter_secure_storage/example/pubspec.yaml +++ b/packages/flutter_secure_storage/example/pubspec.yaml @@ -9,7 +9,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_secure_storage: ^6.0.0 + flutter_secure_storage: ^9.0.0 flutter_secure_storage_tizen: path: ../ diff --git a/packages/flutter_secure_storage/pubspec.yaml b/packages/flutter_secure_storage/pubspec.yaml index b567c2268..ac7da99b3 100644 --- a/packages/flutter_secure_storage/pubspec.yaml +++ b/packages/flutter_secure_storage/pubspec.yaml @@ -2,7 +2,7 @@ name: flutter_secure_storage_tizen description: Tizen implementation of the flutter_secure_storage plugin. homepage: https://github.com/flutter-tizen/plugins repository: https://github.com/flutter-tizen/plugins/tree/master/packages/flutter_secure_storage -version: 0.1.0 +version: 0.1.1 environment: sdk: ">=2.18.0 <4.0.0" @@ -18,7 +18,7 @@ flutter: dependencies: flutter: sdk: flutter - flutter_secure_storage_platform_interface: ^1.0.0 + flutter_secure_storage_platform_interface: ^1.0.1 dev_dependencies: flutter_test: From 8ed18ee9660b0e3749ce4d4523a2a81647713a87 Mon Sep 17 00:00:00 2001 From: Swanseo0 <98388769+Swanseo0@users.noreply.github.com> Date: Wed, 6 Sep 2023 13:47:43 +0900 Subject: [PATCH 2/2] [sqflite] Update sqflite to 2.3.0 (#614) --- packages/sqflite/CHANGELOG.md | 4 +- packages/sqflite/README.md | 4 +- packages/sqflite/analysis_options.yaml | 4 + .../sqflite/example/analysis_options.yaml | 102 ++++++++ .../sqflite/example/lib/batch_test_page.dart | 49 +++- .../example/lib/database/database.dart | 27 +-- .../example/lib/database/database_impl.dart | 90 +++++++ .../example/lib/database/database_io.dart | 86 +++++++ .../example/lib/database/database_web.dart | 5 + .../example/lib/exception_test_page.dart | 9 +- .../sqflite/example/lib/exp_test_page.dart | 226 ++++++++++++++---- packages/sqflite/example/lib/main.dart | 21 +- .../sqflite/example/lib/manual_test_page.dart | 100 +++++++- packages/sqflite/example/lib/model/item.dart | 2 +- .../sqflite/example/lib/open_test_page.dart | 226 +++++++++++------- .../sqflite/example/lib/raw_test_page.dart | 180 ++++++++++---- .../sqflite/example/lib/slow_test_page.dart | 9 +- .../example/lib/src/common_import.dart | 2 + packages/sqflite/example/lib/src/expect.dart | 22 +- .../sqflite/example/lib/src/item_widget.dart | 5 +- .../example/lib/src/main_item_widget.dart | 5 +- packages/sqflite/example/lib/test_page.dart | 3 +- .../sqflite/example/lib/type_test_page.dart | 27 ++- packages/sqflite/example/lib/utils.dart | 11 +- packages/sqflite/example/pubspec.yaml | 4 +- packages/sqflite/pubspec.yaml | 6 +- 26 files changed, 965 insertions(+), 264 deletions(-) create mode 100644 packages/sqflite/example/analysis_options.yaml create mode 100644 packages/sqflite/example/lib/database/database_impl.dart create mode 100644 packages/sqflite/example/lib/database/database_io.dart create mode 100644 packages/sqflite/example/lib/database/database_web.dart diff --git a/packages/sqflite/CHANGELOG.md b/packages/sqflite/CHANGELOG.md index 01ca170a3..9d314d42f 100644 --- a/packages/sqflite/CHANGELOG.md +++ b/packages/sqflite/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 0.1.3 +* Update sqflite to 2.3.0. * Increase the minimum Flutter version to 3.3. +* Increase the minimum SDK version to 3.0.0. ## 0.1.2 diff --git a/packages/sqflite/README.md b/packages/sqflite/README.md index 0b814d75f..7787a4409 100644 --- a/packages/sqflite/README.md +++ b/packages/sqflite/README.md @@ -10,8 +10,8 @@ This package is not an _endorsed_ implementation of `sqflite`. Therefore, you ha ```yaml dependencies: - sqflite: ^2.2.8 - sqflite_tizen: ^0.1.2 + sqflite: ^2.3.0 + sqflite_tizen: ^0.1.3 ``` Then you can import `sqflite` in your Dart code: diff --git a/packages/sqflite/analysis_options.yaml b/packages/sqflite/analysis_options.yaml index cf5ccebd4..1e00f70fd 100644 --- a/packages/sqflite/analysis_options.yaml +++ b/packages/sqflite/analysis_options.yaml @@ -11,6 +11,10 @@ include: package:flutter_lints/flutter.yaml # https://github.com/flutter/flutter/blob/master/analysis_options.yaml analyzer: + language: + strict-casts: true + strict-inference: true + errors: # treat missing required parameters as a warning (not a hint) missing_required_param: warning diff --git a/packages/sqflite/example/analysis_options.yaml b/packages/sqflite/example/analysis_options.yaml new file mode 100644 index 000000000..2be3bca39 --- /dev/null +++ b/packages/sqflite/example/analysis_options.yaml @@ -0,0 +1,102 @@ +include: package:flutter_lints/flutter.yaml + +# Until there are meta linter rules, each desired lint must be explicitly enabled. +# See: https://github.com/dart-lang/linter/issues/288 +# +# For a list of lints, see: http://dart-lang.github.io/linter/lints/ +# See the configuration guide for more +# https://github.com/dart-lang/sdk/tree/master/pkg/analyzer#configuring-the-analyzer +# +# NOTE: Please keep this file in sync with +# https://github.com/flutter/flutter/blob/master/analysis_options.yaml + +analyzer: + language: + strict-casts: true + strict-inference: true + + errors: + # treat missing required parameters as a warning (not a hint) + missing_required_param: warning + # treat missing returns as a warning (not a hint) + missing_return: warning + # allow having TODOs in the code + todo: ignore + # Ignore errors like + # 'super_goes_last' is a deprecated lint rule and should not be used • included_file_warning + included_file_warning: ignore + +linter: + rules: + - always_declare_return_types + - avoid_dynamic_calls + - avoid_empty_else + - avoid_relative_lib_imports + - avoid_shadowing_type_parameters + - avoid_slow_async_io + - avoid_types_as_parameter_names + - await_only_futures + - camel_case_extensions + - camel_case_types + - cancel_subscriptions + - curly_braces_in_flow_control_structures + - directives_ordering + - empty_catches + - hash_and_equals + - collection_methods_unrelated_type + - no_adjacent_strings_in_list + - no_duplicate_case_values + - non_constant_identifier_names + - omit_local_variable_types + - package_api_docs + - package_prefixed_library_names + - prefer_generic_function_type_aliases + - prefer_is_empty + - prefer_is_not_empty + - prefer_iterable_whereType + - prefer_single_quotes + - prefer_typing_uninitialized_variables + - sort_child_properties_last + - test_types_in_equals + - throw_in_finally + - unawaited_futures + - unnecessary_null_aware_assignments + - unnecessary_statements + - unrelated_type_equality_checks + - unsafe_html + - valid_regexps + + - constant_identifier_names + - control_flow_in_finally + - empty_statements + - implementation_imports + - overridden_fields + - package_names + - prefer_const_constructors + - prefer_initializing_formals + - prefer_void_to_null + # + - always_require_non_null_named_parameters + - annotate_overrides + - avoid_init_to_null + - avoid_null_checks_in_equality_operators + - avoid_return_types_on_setters + - empty_constructor_bodies + - library_names + - library_prefixes + - prefer_adjacent_string_concatenation + - prefer_collection_literals + - prefer_contains + - slash_for_doc_comments + - type_init_formals + - unnecessary_const + - unnecessary_new + - unnecessary_null_in_if_null_operators + - use_rethrow_when_possible + # === doc rules === + - public_member_api_docs + # + # - prefer_final_locals + - sort_constructors_first + - sort_unnamed_constructors_first + diff --git a/packages/sqflite/example/lib/batch_test_page.dart b/packages/sqflite/example/lib/batch_test_page.dart index 2f3f1c66f..66cb011c3 100644 --- a/packages/sqflite/example/lib/batch_test_page.dart +++ b/packages/sqflite/example/lib/batch_test_page.dart @@ -9,7 +9,7 @@ class BatchTestPage extends TestPage { BatchTestPage({Key? key}) : super('Batch tests', key: key) { test('BatchQuery', () async { // await Sqflite.devSetDebugModeOn(); - final path = await initDeleteDb('batch.db'); + final path = await initDeleteDb('batch_query.db'); final db = await openDatabase(path); // empty batch @@ -42,7 +42,7 @@ class BatchTestPage extends TestPage { await db.close(); }); test('Batch', () async { - // await Sqflite.devSetDebugModeOn(); + // await databaseFactory.devSetDebugModeOn(); final path = await initDeleteDb('batch.db'); final db = await openDatabase(path); @@ -50,7 +50,7 @@ class BatchTestPage extends TestPage { var batch = db.batch(); var results = await batch.commit(); expect(results.length, 0); - expect(results, []); + expect(results, isEmpty); // one create table batch = db.batch(); @@ -112,7 +112,7 @@ class BatchTestPage extends TestPage { where: 'name = ?', whereArgs: ['item']); batch.delete('Test', where: 'name = ?', whereArgs: ['item']); results = await batch.commit(noResult: true); - expect(results, []); + expect(results, isEmpty); await db.close(); }); @@ -139,6 +139,47 @@ class BatchTestPage extends TestPage { await db.close(); }); + test('Apply in database', () async { + // await Sqflite.devSetDebugModeOn(); + final path = await initDeleteDb('apply_in_database.db'); + final db = await openDatabase(path); + + late List results; + + final batch1 = db.batch(); + batch1.execute('CREATE TABLE Test (id INTEGER PRIMARY KEY, name TEXT)'); + final batch2 = db.batch(); + batch2.rawInsert('INSERT INTO Test (name) VALUES (?)', ['item1']); + results = await batch1.apply(); + expect(results, [null]); + + results = await batch2.apply(); + expect(results, [1]); + await db.close(); + }); + + test('Apply in transaction', () async { + // await Sqflite.devSetDebugModeOn(); + final path = await initDeleteDb('apply_in_transaction.db'); + final db = await openDatabase(path); + + late List results; + + await db.transaction((txn) async { + final batch1 = txn.batch(); + batch1.execute('CREATE TABLE Test (id INTEGER PRIMARY KEY, name TEXT)'); + final batch2 = txn.batch(); + batch2.rawInsert('INSERT INTO Test (name) VALUES (?)', ['item1']); + results = await batch1.apply(); + expect(results, [null]); + + results = await batch2.apply(); + expect(results, [1]); + }); + + await db.close(); + }); + test('Batch continue on error', () async { // await Sqflite.devSetDebugModeOn(); final path = await initDeleteDb('batch_continue_on_error.db'); diff --git a/packages/sqflite/example/lib/database/database.dart b/packages/sqflite/example/lib/database/database.dart index b8fb4877b..0fb2b9b9f 100644 --- a/packages/sqflite/example/lib/database/database.dart +++ b/packages/sqflite/example/lib/database/database.dart @@ -1,25 +1,2 @@ -import 'dart:async'; -import 'dart:io'; - -import 'package:path/path.dart'; -import 'package:sqflite/sqflite.dart'; - -/// delete the db, create the folder and returns its path -Future initDeleteDb(String dbName) async { - final databasePath = await getDatabasesPath(); - final path = join(databasePath, dbName); - - // make sure the folder exists - // ignore: avoid_slow_async_io - if (await Directory(dirname(path)).exists()) { - await deleteDatabase(path); - } else { - try { - await Directory(dirname(path)).create(recursive: true); - } catch (e) { - // ignore: avoid_print - print(e); - } - } - return path; -} +export 'database_impl.dart'; +export 'database_io.dart' if (dart.library.html) 'database_web.dart'; diff --git a/packages/sqflite/example/lib/database/database_impl.dart b/packages/sqflite/example/lib/database/database_impl.dart new file mode 100644 index 000000000..ab9c3a028 --- /dev/null +++ b/packages/sqflite/example/lib/database/database_impl.dart @@ -0,0 +1,90 @@ +import 'dart:typed_data'; + +import 'package:sqflite/sqflite.dart'; + +import 'database.dart'; + +/// Custom platform Handler, need to handle Web or IO differently or from a +/// custom app +abstract class PlatformHandler { + /// delete the db, create the folder and returns its path + Future initDeleteDb(String dbName) async { + if (await databaseExists(dbName)) { + await deleteDatabase(dbName); + } + return dbName; + } + + /// Write the db file directly to the file system + Future writeFileAsBytes(String path, List bytes, + {bool flush = false}); + + /// Read a file as bytes + Future readFileAsBytes(String path); + + /// Write a file as a string + Future writeFileAsString(String path, String text, + {bool flush = false}); + + /// Read a file as a string + Future readFileAsString(String path); + + /// Check if a path exists. + Future pathExists(String path); + + /// Recursively create a directory + Future createDirectory(String path); + + /// Recursively delete a directory + Future deleteDirectory(String path); + + /// Check if a directory exists + Future existsDirectory(String path); +} + +// --- +// Compat, to keep the example page as is +// --- + +/// delete the db, create the folder and returnes its path +Future initDeleteDb(String dbName) => + platformHandler.initDeleteDb(dbName); + +/// Write the db file directly to the file system +Future writeFileAsBytes(String path, List bytes, + {bool flush = false}) => + platformHandler.writeFileAsBytes(path, bytes, flush: flush); + +/// Read a file as bytes +Future readFileAsBytes(String path) => + platformHandler.readFileAsBytes(path); + +/// Write a file as a string +Future writeFileAsString(String path, String text, + {bool flush = false}) => + platformHandler.writeFileAsString(path, text, flush: flush); + +/// Read a file as a string +Future readFileAsString(String path) => + platformHandler.readFileAsString(path); + +/// Check if a path exists. +Future pathExists(String path) => platformHandler.pathExists(path); + +/// Recursively create a directory +Future createDirectory(String path) => + platformHandler.createDirectory(path); + +/// Recursively delete a directory +Future deleteDirectory(String path) => + platformHandler.deleteDirectory(path); + +/// Check if a directory exists +Future existsDirectory(String path) => + platformHandler.existsDirectory(path); + +PlatformHandler? _platformHandler; + +/// Platform handler (can be overriden, needed for the web test app) +PlatformHandler get platformHandler => _platformHandler ??= platformHandlerIo; +set platformHandler(PlatformHandler handler) => _platformHandler = handler; diff --git a/packages/sqflite/example/lib/database/database_io.dart b/packages/sqflite/example/lib/database/database_io.dart new file mode 100644 index 000000000..52d22eefe --- /dev/null +++ b/packages/sqflite/example/lib/database/database_io.dart @@ -0,0 +1,86 @@ +import 'dart:async'; +import 'dart:io'; +import 'dart:typed_data'; + +import 'package:path/path.dart'; +import 'package:sqflite/sqflite.dart'; +import 'package:sqflite_tizen_example/database/database.dart'; + +class _PlatformHandlerIo extends PlatformHandler { + /// delete the db, create the folder and returns its path + @override + Future initDeleteDb(String dbName) async { + final databasePath = await getDatabasesPath(); + // print(databasePath); + final path = join(databasePath, dbName); + + // make sure the folder exists + // ignore: avoid_slow_async_io + if (await Directory(dirname(path)).exists()) { + await deleteDatabase(path); + } else { + try { + await Directory(dirname(path)).create(recursive: true); + } catch (e) { + // ignore: avoid_print + print(e); + } + } + return path; + } + + /// Write the db file directly to the file system + @override + Future writeFileAsBytes(String path, List bytes, + {bool flush = false}) async { + await File(path).writeAsBytes(bytes, flush: flush); + } + + /// Read a file as bytes + @override + Future readFileAsBytes(String path) async { + return File(path).readAsBytes(); + } + + /// Write a file as a string + @override + Future writeFileAsString(String path, String text, + {bool flush = false}) async { + await File(path).writeAsString(text, flush: true); + } + + /// Read a file as a string + @override + Future readFileAsString(String path) async { + return File(path).readAsString(); + } + + /// Check if a path exists. + @override + Future pathExists(String path) async { + // ignore: avoid_slow_async_io + return File(path).exists(); + } + + /// Recursively create a directory + @override + Future createDirectory(String path) async { + await Directory(dirname(path)).create(recursive: true); + } + + /// Recursively delete a directory + @override + Future deleteDirectory(String path) async { + await Directory(path).delete(recursive: true); + } + + /// Check if a directory exists + @override + Future existsDirectory(String path) async { + // ignore: avoid_slow_async_io + return Directory(path).exists(); + } +} + +/// Io platform handler +PlatformHandler platformHandlerIo = _PlatformHandlerIo(); diff --git a/packages/sqflite/example/lib/database/database_web.dart b/packages/sqflite/example/lib/database/database_web.dart new file mode 100644 index 000000000..e303b693a --- /dev/null +++ b/packages/sqflite/example/lib/database/database_web.dart @@ -0,0 +1,5 @@ +import 'package:sqflite_tizen_example/database/database.dart'; + +/// platform handler io not supported on the web. +PlatformHandler get platformHandlerIo => + throw UnsupportedError('platform handler io not supported on the web'); diff --git a/packages/sqflite/example/lib/exception_test_page.dart b/packages/sqflite/example/lib/exception_test_page.dart index 7c49e5ecf..e360990f6 100644 --- a/packages/sqflite/example/lib/exception_test_page.dart +++ b/packages/sqflite/example/lib/exception_test_page.dart @@ -1,11 +1,8 @@ -import 'dart:async'; -import 'dart:convert'; -import 'dart:io'; - import 'package:flutter/foundation.dart'; import 'package:sqflite/sqflite.dart'; import 'package:sqflite/sql.dart'; +import 'src/common_import.dart'; import 'test_page.dart'; // ignore_for_file: avoid_print @@ -364,7 +361,7 @@ class ExceptionTestPage extends TestPage { }); test('Bind no argument (no iOS)', () async { - if (!Platform.isIOS) { + if (!platform.isIOS) { // await Sqflite.devSetDebugModeOn(true); final path = await initDeleteDb('bind_no_arg_failed.db'); final db = await openDatabase(path); @@ -383,7 +380,7 @@ class ExceptionTestPage extends TestPage { test('crash ios (no iOS)', () async { // This crashes natively on iOS...can't catch it yet - if (!Platform.isIOS) { + if (!platform.isIOS) { //if (true) { // await Sqflite.devSetDebugModeOn(true); final path = await initDeleteDb('bind_no_arg_failed.db'); diff --git a/packages/sqflite/example/lib/exp_test_page.dart b/packages/sqflite/example/lib/exp_test_page.dart index 326b7cacc..24b2e451f 100644 --- a/packages/sqflite/example/lib/exp_test_page.dart +++ b/packages/sqflite/example/lib/exp_test_page.dart @@ -1,6 +1,3 @@ -import 'dart:async'; -import 'dart:convert'; -import 'dart:io'; import 'dart:isolate'; import 'dart:typed_data'; @@ -9,6 +6,8 @@ import 'package:flutter/services.dart'; import 'package:path/path.dart'; import 'package:sqflite/sqflite.dart'; import 'package:sqflite_common/sqflite_dev.dart'; +import 'package:sqflite_tizen_example/src/common_import.dart'; +import 'package:sqflite_tizen_example/utils.dart'; import 'test_page.dart'; @@ -63,6 +62,7 @@ class ExpTestPage extends TestPage { }); test('in', () async { + //await Sqflite.devSetDebugModeOn(true); final path = await initDeleteDb('simple_exp.db'); final db = await openDatabase(path); @@ -101,6 +101,7 @@ class ExpTestPage extends TestPage { }); test('Raw escaping', () async { + //await Sqflite.devSetDebugModeOn(true); final path = await initDeleteDb('raw_escaping_fields.db'); final db = await openDatabase(path); @@ -129,6 +130,7 @@ class ExpTestPage extends TestPage { }); test('Escaping fields', () async { + //await Sqflite.devSetDebugModeOn(true); final path = await initDeleteDb('escaping_fields.db'); final db = await openDatabase(path); @@ -155,6 +157,7 @@ class ExpTestPage extends TestPage { }); test('Functions', () async { + //await Sqflite.devSetDebugModeOn(true); final path = await initDeleteDb('exp_functions.db'); final db = await openDatabase(path); @@ -199,6 +202,7 @@ class ExpTestPage extends TestPage { }); test('Alias', () async { + //await Sqflite.devSetDebugModeOn(true); final path = await initDeleteDb('exp_alias.db'); final db = await openDatabase(path); @@ -221,6 +225,7 @@ class ExpTestPage extends TestPage { }); test('Dart2 query', () async { + // await Sqflite.devSetDebugModeOn(true); final path = await initDeleteDb('exp_dart2_query.db'); final db = await openDatabase(path); @@ -280,6 +285,7 @@ class ExpTestPage extends TestPage { return rawResult; */ test('Issue#48', () async { + // Sqflite.devSetDebugModeOn(true); // devPrint('issue #48'); // Try to query on a non-indexed field final path = await initDeleteDb('exp_issue_48.db'); @@ -339,10 +345,10 @@ class ExpTestPage extends TestPage { await deleteDatabase(path); // Copy from asset - final data = await rootBundle.load(join('assets', 'issue_64.db')); + final data = await rootBundle.load(url.join('assets', 'issue_64.db')); final bytes = data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes); - await File(path).writeAsBytes(bytes); + await databaseFactory.writeDatabaseBytes(path, bytes); // open the database final db = await openDatabase(path); @@ -387,17 +393,17 @@ INSERT INTO test (value) VALUES (1); INSERT INTO test (value) VALUES (10); '''; await db.execute(sql); - // that should be the expected result // var expectedResult = [ // {'value': 1}, // {'value': 10} // ]; final result = await db.rawQuery('SELECT * FROM $table'); + print(json.encode(result)); + // However (at least on Android) // result is empty, only the first statement is executed - print(json.encode(result)); - expect(result, []); + expect(result, isEmpty); } finally { await db.close(); } @@ -470,46 +476,80 @@ CREATE TABLE test ( } }); - test('Issue#206', () async { - final path = await initDeleteDb('issue_206.db'); + test('ATTACH database', () async { + final db1Path = await initDeleteDb('attach1.db'); + final db2Path = await initDeleteDb('attach2.db'); - final db = await openDatabase(path); + // Create some data on db1 and close it + var db1 = await databaseFactory.openDatabase(db1Path); try { - final sqls = LineSplitter.split( - '''CREATE VIRTUAL TABLE Food using fts4(description TEXT) - INSERT Into Food (description) VALUES ('banana') - INSERT Into Food (description) VALUES ('apple')'''); - final batch = db.batch(); - for (var sql in sqls) { - batch.execute(sql); - } + var batch = db1.batch(); + batch.execute('CREATE TABLE table1 (col1 INTEGER)'); + batch.insert('table1', {'col1': 1234}); await batch.commit(); + } finally { + await db1.close(); + } - final results = await db.rawQuery( - 'SELECT description, matchinfo(Food) as matchinfo FROM Food WHERE Food MATCH ?', - ['ban*']); - print(results); - // matchinfo is currently returned as binary bloc - expect(results.length, 1); - final map = results.first; - final matchInfo = map['matchinfo'] as Uint8List; - - // Convert to Uint32List - final uint32ListLength = matchInfo.length ~/ 4; - final uint32List = Uint32List(uint32ListLength); - final data = ByteData.view( - matchInfo.buffer, matchInfo.offsetInBytes, matchInfo.length); - for (var i = 0; i < uint32ListLength; i++) { - uint32List[i] = data.getUint32(i * 4, Endian.host); - } - // print(uint32List); - expect(uint32List, [1, 1, 1, 1, 1]); - expect(map['matchinfo'], const TypeMatcher()); + // Open a new db2 database, attach db1 and query it + + var db2 = await databaseFactory.openDatabase(db2Path); + try { + await db2.execute('ATTACH DATABASE \'$db1Path\' AS db1'); + var rows = await db2.query('db1.table1'); + expect(rows, [ + {'col1': 1234} + ]); } finally { - await db.close(); + await db2.close(); } }); + /// fts4 + var fts4Supports = supportsCompatMode; + if (fts4Supports) { + test('Issue#206', () async { + //await Sqflite.devSetDebugModeOn(true); + final path = await initDeleteDb('issue_206.db'); + + final db = await openDatabase(path); + try { + final sqls = LineSplitter.split( + '''CREATE VIRTUAL TABLE Food using fts4(description TEXT) + INSERT Into Food (description) VALUES ('banana') + INSERT Into Food (description) VALUES ('apple')'''); + final batch = db.batch(); + for (var sql in sqls) { + batch.execute(sql); + } + await batch.commit(); + + final results = await db.rawQuery( + 'SELECT description, matchinfo(Food) as matchinfo FROM Food WHERE Food MATCH ?', + ['ban*']); + print(results); + // matchinfo is currently returned as binary bloc + expect(results.length, 1); + final map = results.first; + final matchInfo = map['matchinfo'] as Uint8List; + + // Convert to Uint32List + final uint32ListLength = matchInfo.length ~/ 4; + final uint32List = Uint32List(uint32ListLength); + final data = ByteData.view( + matchInfo.buffer, matchInfo.offsetInBytes, matchInfo.length); + for (var i = 0; i < uint32ListLength; i++) { + uint32List[i] = data.getUint32(i * 4, Endian.host); + } + // print(uint32List); + expect(uint32List, [1, 1, 1, 1, 1]); + expect(map['matchinfo'], const TypeMatcher()); + } finally { + await db.close(); + } + }); + } + test('Log level', () async { // test setting log level Database? db; @@ -533,11 +573,81 @@ CREATE TABLE test ( } }); + Future testBigBlog(int size) async { + // await Sqflite.devSetDebugModeOn(true); + final path = await initDeleteDb('big_blob.db'); + var db = await openDatabase(path, version: 1, + onCreate: (Database db, int version) async { + await db + .execute('CREATE TABLE Test (id INTEGER PRIMARY KEY, value BLOB)'); + }); + try { + var blob = + Uint8List.fromList(List.generate(size, (index) => index % 256)); + var id = await db.insert('Test', {'value': blob}); + + /// Get the value field from a given id + Future getValue(int id) async { + return ((await db.query('Test', where: 'id = $id')).first)['value'] + as Uint8List; + } + + expect((await getValue(id)).length, blob.length); + } finally { + await db.close(); + } + } + + // We don't test automatically above as it crashes seriously on Android + test('big blob 800 Ko', () async { + await testBigBlog(800000); + }); + + Future testBigText(int size) async { + // await Sqflite.devSetDebugModeOn(true); + final path = await initDeleteDb('big_text.db'); + var db = await openDatabase(path, version: 1, + onCreate: (Database db, int version) async { + await db + .execute('CREATE TABLE Test (id INTEGER PRIMARY KEY, value TEXT)'); + }); + try { + var text = List.generate(size, (index) => 'A').join(); + var id = await db.insert('Test', {'value': text}); + + /// Get the value field from a given id + Future getValue(int id) async { + return ((await db.query('Test', where: 'id = $id')).first)['value'] + as String; + } + + expect((await getValue(id)).length, text.length); + } finally { + await db.close(); + } + } + + // We don't test automatically above as it crashes seriously on Android + test('big text 800 Ko', () async { + await testBigText(800000); + }); + /* + test('big blob 1500 Ko (fails on Android sqlite)', () async { + await testBigBlog(1500000); + }); + test('big blob 2 Mo (fails on Android sqlite)', () async { + await testBigBlog(2000000); + }); + test('big blob 15 Mo (fails on Android sqlite)', () async { + await testBigBlog(15000000); + }); + */ /* test('Isolate', () async { // This test does not work yet // Need background registration. I Kept the code for future reference await Future.sync(() async { + // await Sqflite.devSetDebugModeOn(true); final path = await initDeleteDb('isolate.db'); // Open the db in the main isolate @@ -558,7 +668,7 @@ CREATE TABLE test ( int index = 0; SendPort sendPort; List> results; - var completer = Completer(); + var completer = Completer(); var subscription = receivePort.listen((data) { switch (index++) { case 0: @@ -588,6 +698,40 @@ CREATE TABLE test ( }).timeout(Duration(seconds: 3)); }); */ + test('missing parameter', () async { + var db = await openDatabase(inMemoryDatabasePath); + await db.execute( + 'CREATE TABLE IF NOT EXISTS foo (id int primary key, name text)'); + var missingParameterShouldFail = !supportsCompatMode; + try { + await db.rawQuery('SELECT * FROM foo WHERE id=?'); + } catch (e) { + expect(missingParameterShouldFail, isTrue); + } + await db.close(); + }); + // Issue https://github.com/tekartik/sqflite/issues/929 + // Pragma has to use rawQuery...why, on sqflite Android + test('wal', () async { + // await Sqflite.devSetDebugModeOn(true); + var db = await openDatabase(inMemoryDatabasePath); + try { + await db.execute('PRAGMA journal_mode=WAL'); + } catch (e) { + print(e); + await db.rawQuery('PRAGMA journal_mode=WAL'); + } + await db.execute('CREATE TABLE test (id INTEGER)'); + await db.insert('test', {'id': 1}); + try { + var resultSet = await db.rawQuery('SELECT id FROM test'); + expect(resultSet, [ + {'id': 1}, + ]); + } finally { + await db.close(); + } + }); } } diff --git a/packages/sqflite/example/lib/main.dart b/packages/sqflite/example/lib/main.dart index 25d2d5d32..f395209c1 100644 --- a/packages/sqflite/example/lib/main.dart +++ b/packages/sqflite/example/lib/main.dart @@ -17,18 +17,27 @@ import 'todo_test_page.dart'; import 'type_test_page.dart'; void main() { - runApp(const MyApp()); + mainExampleApp(); +} + +/// Example app main entry point, exported for external application +/// +/// might move to a different shared package. +void mainExampleApp() { + WidgetsFlutterBinding.ensureInitialized(); + // debugAutoStartRouteName = testOpenRoute; + runApp(const SqfliteExampleApp()); } /// Sqflite test app -class MyApp extends StatefulWidget { +class SqfliteExampleApp extends StatefulWidget { /// test app. - const MyApp({Key? key}) : super(key: key); + const SqfliteExampleApp({Key? key}) : super(key: key); // This widget is the root of your application. @override // ignore: library_private_types_in_public_api - _MyAppState createState() => _MyAppState(); + _SqfliteExampleAppState createState() => _SqfliteExampleAppState(); } /// Simple test page. @@ -61,7 +70,7 @@ const String testExpRoute = '/test/exp'; /// Deprecated test page. const String testDeprecatedRoute = '/test/deprecated'; -class _MyAppState extends State { +class _SqfliteExampleAppState extends State { var routes = { '/test': (BuildContext context) => MyHomePage(), testRawRoute: (BuildContext context) => RawTestPage(), @@ -155,7 +164,7 @@ class _MyHomePageState extends State { void initState() { super.initState(); - Future.delayed(Duration.zero).then((_) async { + Future.delayed(Duration.zero).then((_) async { if (mounted) { // Use it to auto start a test page if (debugAutoStartRouteName != null) { diff --git a/packages/sqflite/example/lib/manual_test_page.dart b/packages/sqflite/example/lib/manual_test_page.dart index 187aa43e2..85f9b2b8c 100644 --- a/packages/sqflite/example/lib/manual_test_page.dart +++ b/packages/sqflite/example/lib/manual_test_page.dart @@ -1,4 +1,4 @@ -import 'dart:async'; +import 'dart:typed_data'; import 'package:flutter/material.dart'; import 'package:sqflite/sqflite.dart'; @@ -8,10 +8,11 @@ import 'package:sqflite/utils/utils.dart'; import 'package:sqflite_tizen_example/src/item_widget.dart'; import 'package:sqflite_tizen_example/utils.dart'; -import 'model/item.dart'; - // ignore_for_file: avoid_print +import 'model/item.dart'; +import 'src/common_import.dart'; + /// Manual test page. class ManualTestPage extends StatefulWidget { /// Test page. @@ -26,6 +27,13 @@ class _ManualTestPageState extends State { Database? database; static const String dbName = 'manual_test.db'; + Future showToast(String message) async { + ScaffoldMessenger.of(context) + ..clearSnackBars() + ..showSnackBar(SnackBar( + content: Text(message), duration: const Duration(milliseconds: 300))); + } + Future _openDatabase() async { return database ??= await databaseFactory.openDatabase(dbName); } @@ -52,13 +60,59 @@ class _ManualTestPageState extends State { return true; } + Future _addAndQuery({int? msDelay, bool? noSynchronized}) async { + // await databaseFactory.debugSetLogLevel(sqfliteLogLevelVerbose); + var db = await _openDatabase(); + + // ignore: invalid_use_of_visible_for_testing_member + db.internalsDoNotUseSynchronized = noSynchronized ?? false; + await db.transaction((txn) async { + await txn.execute( + 'CREATE TABLE IF NOT EXISTS Task(id INTEGER PRIMARY KEY, name TEXT)'); + await txn.execute('INSERT INTO Task(name) VALUES (?)', + ['task ${DateTime.now().toIso8601String()}']); + var count = + firstIntValue(await txn.query('Task', columns: [sqlCountColumn])); + unawaited(showToast('$count task(s)')); + if (msDelay != null) { + await Future.delayed(Duration(milliseconds: msDelay)); + } + }); + } + @override void didChangeDependencies() { super.didChangeDependencies(); items = [ + SqfMenuItem('SQLite version', () async { + final db = await openDatabase(inMemoryDatabasePath); + + final results = await db.rawQuery('select sqlite_version()'); + print('select sqlite_version(): $results'); + var version = results.first.values.first; + print('sqlite version: $version'); + await db.close(); + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + content: Text('select sqlite_version(): $version'), + )); + } + }, summary: 'select sqlite_version()'), + SqfMenuItem('Factory information', () async { + var info = databaseFactory.toString(); + print('sqlite database factory: $info'); + unawaited(showToast(info)); + }, summary: 'toString()'), SqfMenuItem('openDatabase', () async { await _openDatabase(); }, summary: 'Open the database'), + SqfMenuItem('transaction add and query and pause', () async { + await _addAndQuery(msDelay: 5000); + }, summary: 'open/create table/add/query/pause'), + SqfMenuItem('transaction add and query and pause no synchronized', + () async { + await _addAndQuery(msDelay: 5000, noSynchronized: true); + }, summary: 'open/create table/add/query/pause'), SqfMenuItem('BEGIN EXCLUSIVE', () async { final db = await _openDatabase(); await db.execute('BEGIN EXCLUSIVE'); @@ -99,16 +153,48 @@ class _ManualTestPageState extends State { print(info.toString()); }, summary: 'Implementation info (dev only)'), SqfMenuItem('Increment version', () async { - await _incrementVersion(); + print(await _incrementVersion()); }, summary: 'Implementation info (dev only)'), SqfMenuItem('Multiple db', () async { - await Navigator.of(context).push(MaterialPageRoute(builder: (_) { + await Navigator.of(context).push(MaterialPageRoute(builder: (_) { return const MultipleDbTestPage(); })); - }, summary: 'Open multiple databases') + }, summary: 'Open multiple databases'), + ...[800000, 1500000, 15000000, 150000000] + .map((size) => SqfMenuItem('Big blob $size', () async { + await testBigBlog(size); + })) ]; } + Future testBigBlog(int size) async { + // await Sqflite.devSetDebugModeOn(true); + var db = await openDatabase(inMemoryDatabasePath, version: 1, + onCreate: (Database db, int version) async { + await db + .execute('CREATE TABLE Test (id INTEGER PRIMARY KEY, value BLOB)'); + }); + try { + var blob = + Uint8List.fromList(List.generate(size, (index) => index % 256)); + var id = await db.insert('Test', {'value': blob}); + + /// Get the value field from a given id + Future getValue(int id) async { + return ((await db.query('Test', where: 'id = $id')).first)['value'] + as Uint8List; + } + + var ok = (await getValue(id)).length == blob.length; + if (mounted) { + ScaffoldMessenger.of(context) + .showSnackBar(SnackBar(content: Text('$size: $ok'))); + } + } finally { + await db.close(); + } + } + @override Widget build(BuildContext context) { itemWidgets = items @@ -154,7 +240,7 @@ class MultipleDbTestPage extends StatelessWidget { return ListTile( title: Text(name), onTap: () { - Navigator.of(context).push(MaterialPageRoute(builder: (_) { + Navigator.of(context).push(MaterialPageRoute(builder: (_) { return SimpleDbTestPage( dbName: name, ); diff --git a/packages/sqflite/example/lib/model/item.dart b/packages/sqflite/example/lib/model/item.dart index a6437ea97..ee0ac3df3 100644 --- a/packages/sqflite/example/lib/model/item.dart +++ b/packages/sqflite/example/lib/model/item.dart @@ -40,7 +40,7 @@ class SqfMenuItem extends Item { /// Run the item. Future run() { state = ItemState.running; - return Future.delayed(const Duration()).then((_) async { + return Future.delayed(const Duration()).then((_) async { try { await body(); state = ItemState.success; diff --git a/packages/sqflite/example/lib/open_test_page.dart b/packages/sqflite/example/lib/open_test_page.dart index 27f68adf9..c5835a7a7 100644 --- a/packages/sqflite/example/lib/open_test_page.dart +++ b/packages/sqflite/example/lib/open_test_page.dart @@ -1,6 +1,3 @@ -import 'dart:async'; -import 'dart:io'; - import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart' show rootBundle; import 'package:flutter/services.dart'; @@ -12,11 +9,10 @@ import 'package:sqflite/src/database_mixin.dart' // ignore: implementation_impor import 'package:sqflite/src/factory_mixin.dart' // ignore: implementation_imports show SqfliteDatabaseFactoryMixin; -import 'package:sqflite_tizen_example/src/dev_utils.dart'; import 'package:synchronized/synchronized.dart'; +import 'src/common_import.dart'; import 'test_page.dart'; - // ignore_for_file: avoid_slow_async_io // ignore_for_file: avoid_print @@ -25,7 +21,7 @@ class OpenCallbacks { /// Open callbacks. OpenCallbacks() { onConfigure = (Database db) { - //print('onConfigure'); + // devPrint('onConfigure'); //verify(!onConfigureCalled, 'onConfigure must be called once'); expect(onConfigureCalled, false, reason: @@ -139,12 +135,13 @@ class OpenTestPage extends TestPage { final factory = databaseFactory; test('Databases path', () async { + // await Sqflite.devSetDebugModeOn(false); final databasesPath = await factory.getDatabasesPath(); // On Android we know it is current a 'databases' folder in the package folder print('databasesPath: $databasesPath'); - if (Platform.isAndroid) { + if (platform.isAndroid) { expect(basename(databasesPath), 'databases'); - } else if (Platform.isIOS) { + } else if (platform.isIOS) { expect(basename(databasesPath), 'Documents'); } final path = join(databasesPath, 'in_default_directory.db'); @@ -153,33 +150,36 @@ class OpenTestPage extends TestPage { await db.close(); }); test('Delete database', () async { + // await Sqflite.devSetDebugModeOn(false); final path = await initDeleteDb('delete_database.db'); expect(await databaseExists(path), false); final db = await openDatabase(path); await db.close(); - expect((await File(path).exists()), true); + expect((await pathExists(path)), true); expect(await databaseExists(path), true); print('Deleting database $path'); await deleteDatabase(path); - expect((await File(path).exists()), false); + expect((await pathExists(path)), false); expect(await databaseExists(path), false); }); test('Open no version', () async { + //await Sqflite.devSetDebugModeOn(true); final path = await initDeleteDb('open_no_version.db'); - expect((await File(path).exists()), false); + expect((await pathExists(path)), false); final db = await openDatabase(path); - verify(await File(path).exists()); + verify(await pathExists(path)); expect(await db.getVersion(), 0); await db.close(); }); test('isOpen', () async { + //await Sqflite.devSetDebugModeOn(true); final path = await initDeleteDb('is_open.db'); - expect((await File(path).exists()), false); + expect((await pathExists(path)), false); final db = await openDatabase(path); expect(db.isOpen, true); - verify(await File(path).exists()); + verify(await pathExists(path)); await db.close(); expect(db.isOpen, false); }); @@ -187,7 +187,7 @@ class OpenTestPage extends TestPage { test('Open no version onCreate', () async { // should fail final path = await initDeleteDb('open_no_version_on_create.db'); - verify(!(await File(path).exists())); + verify(!(await pathExists(path))); Database? db; try { db = await openDatabase(path, onCreate: (Database db, int version) { @@ -196,11 +196,12 @@ class OpenTestPage extends TestPage { }); verify(false); } on ArgumentError catch (_) {} - verify(!await File(path).exists()); + verify(!await pathExists(path)); expect(db, null); }); test('Open onCreate', () async { + // await Sqflite.devSetDebugModeOn(true); final path = await initDeleteDb('open_test2.db'); var onCreate = false; var onCreateTransaction = false; @@ -221,6 +222,7 @@ class OpenTestPage extends TestPage { }); test('Simple onCreate', () async { + // await Sqflite.devSetDebugModeOn(true); final path = await initDeleteDb('open_simple_on_create.db'); expect(await isDatabase(path), isFalse); @@ -249,6 +251,7 @@ class OpenTestPage extends TestPage { }); test('Open 2 databases', () async { + //await Sqflite.devSetDebugModeOn(true); final path1 = await initDeleteDb('open_db_1.db'); final path2 = await initDeleteDb('open_db_2.db'); final db1 = await openDatabase(path1, version: 1); @@ -258,6 +261,7 @@ class OpenTestPage extends TestPage { }); test('Open onUpgrade', () async { + // await Sqflite.devSetDebugModeOn(true); var onUpgrade = false; final path = await initDeleteDb('open_on_upgrade.db'); var database = await openDatabase(path, version: 1, @@ -293,6 +297,7 @@ class OpenTestPage extends TestPage { }); test('Open onDowngrade', () async { + // await Sqflite.devSetDebugModeOn(true); final path = await initDeleteDb('open_on_downgrade.db'); var database = await openDatabase(path, version: 2, onCreate: (Database db, int version) async { @@ -327,6 +332,7 @@ class OpenTestPage extends TestPage { }); test('Open asset database', () async { + // await Sqflite.devSetDebugModeOn(false); final databasesPath = await getDatabasesPath(); final path = join(databasesPath, 'asset_example.db'); @@ -335,15 +341,14 @@ class OpenTestPage extends TestPage { // Make sure the parent directory exists try { - await Directory(dirname(path)).create(recursive: true); + await createDirectory(path); } catch (_) {} - // Copy from asset - final data = await rootBundle.load(join('assets', 'example.db')); - final List bytes = +// Copy from asset to a database file. + final data = await rootBundle.load(url.join('assets', 'example.db')); + final bytes = data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes); - // Write and flush the bytes written - await File(path).writeAsBytes(bytes, flush: true); + await databaseFactory.writeDatabaseBytes(path, bytes); // open the database final db = await openDatabase(path); @@ -378,6 +383,8 @@ class OpenTestPage extends TestPage { }); test('Open onDowngrade delete', () async { + // await Sqflite.devSetDebugModeOn(false); + final path = await initDeleteDb('open_on_downgrade_delete.db'); var database = await openDatabase(path, version: 3, onCreate: (Database db, int version) async { @@ -432,51 +439,54 @@ class OpenTestPage extends TestPage { }); test('All open callback', () async { + // await Sqflite.devSetDebugModeOn(false); final path = await initDeleteDb('open_all_callbacks.db'); var step = 1; final openCallbacks = OpenCallbacks(); var db = await openCallbacks.open(path, version: 1); - verify(openCallbacks.onConfigureCalled, 'onConfiguredCalled $step'); - verify(openCallbacks.onCreateCalled, 'onCreateCalled $step'); - verify(openCallbacks.onOpenCalled, 'onOpenCalled $step'); - verify(!openCallbacks.onUpgradeCalled!, 'onUpgradeCalled $step'); - verify(!openCallbacks.onDowngradeCalled!, 'onDowngradCalled $step'); - await db.close(); - - ++step; - db = await openCallbacks.open(path, version: 3); - verify(openCallbacks.onConfigureCalled, 'onConfiguredCalled $step'); - verify(!openCallbacks.onCreateCalled!, 'onCreateCalled $step'); - verify(openCallbacks.onOpenCalled, 'onOpenCalled $step'); - verify(openCallbacks.onUpgradeCalled, 'onUpgradeCalled $step'); - verify(!openCallbacks.onDowngradeCalled!, 'onDowngradCalled $step'); - await db.close(); - - ++step; - db = await openCallbacks.open(path, version: 2); - verify(openCallbacks.onConfigureCalled, 'onConfiguredCalled $step'); - verify(!openCallbacks.onCreateCalled!, 'onCreateCalled $step'); - verify(openCallbacks.onOpenCalled, 'onOpenCalled $step'); - verify(!openCallbacks.onUpgradeCalled!, 'onUpgradeCalled $step'); - verify(openCallbacks.onDowngradeCalled, 'onDowngradCalled $step'); - await db.close(); + try { + verify(openCallbacks.onConfigureCalled, 'onConfiguredCalled $step'); + verify(openCallbacks.onCreateCalled, 'onCreateCalled $step'); + verify(openCallbacks.onOpenCalled, 'onOpenCalled $step'); + verify(!openCallbacks.onUpgradeCalled!, 'onUpgradeCalled $step'); + verify(!openCallbacks.onDowngradeCalled!, 'onDowngradeCalled $step'); + await db.close(); - openCallbacks.onDowngrade = onDatabaseDowngradeDelete; - var configureCount = 0; - final callback = openCallbacks.onConfigure; - // allow being called twice - openCallbacks.onConfigure = (Database db) { - if (configureCount == 1) { - openCallbacks.onConfigureCalled = false; - } - configureCount++; - callback!(db); - }; - ++step; - db = await openCallbacks.open(path, version: 1); + ++step; + db = await openCallbacks.open(path, version: 3); + verify(openCallbacks.onConfigureCalled, 'onConfiguredCalled $step'); + verify(!openCallbacks.onCreateCalled!, 'onCreateCalled $step'); + verify(openCallbacks.onOpenCalled, 'onOpenCalled $step'); + verify(openCallbacks.onUpgradeCalled, 'onUpgradeCalled $step'); + verify(!openCallbacks.onDowngradeCalled!, 'onDowngradeCalled $step'); + await db.close(); - /* + ++step; + // devPrint('downgrading'); + db = await openCallbacks.open(path, version: 2); + verify(openCallbacks.onConfigureCalled, 'onConfiguredCalled $step'); + verify(!openCallbacks.onCreateCalled!, 'onCreateCalled $step'); + verify(openCallbacks.onOpenCalled, 'onOpenCalled $step'); + verify(!openCallbacks.onUpgradeCalled!, 'onDowngradeCalled $step'); + verify(openCallbacks.onDowngradeCalled, 'onDowngradCalled $step'); + await db.close(); + // devPrint('downgrading delete'); + openCallbacks.onDowngrade = onDatabaseDowngradeDelete; + var configureCount = 0; + final callback = openCallbacks.onConfigure; + // allow being called twice + openCallbacks.onConfigure = (Database db) { + if (configureCount == 1) { + openCallbacks.onConfigureCalled = false; + } + configureCount++; + callback!(db); + }; + ++step; + db = await openCallbacks.open(path, version: 1); + + /* verify(openCallbacks.onConfigureCalled,'onConfiguredCalled $step'); verify(configureCount == 2, 'onConfigure count'); verify(openCallbacks.onCreateCalled, 'onCreateCalled $step'); @@ -484,10 +494,13 @@ class OpenTestPage extends TestPage { verify(!openCallbacks.onUpgradeCalled, 'onUpgradeCalled $step'); verify(!openCallbacks.onDowngradeCalled, 'onDowngradCalled $step'); */ - await db.close(); + } finally { + await db.close(); + } }); test('Open batch', () async { + // await Sqflite.devSetDebugModeOn(true); final path = await initDeleteDb('open_batch.db'); Future onConfigure(Database db) async { @@ -498,13 +511,15 @@ class OpenTestPage extends TestPage { Future onCreate(Database db, int version) async { final batch = db.batch(); - batch.rawInsert('INSERT INTO Test(value) VALUES("value1")'); + // await db.execute('INSERT INTO Test(value) VALUES("value1")'); This does not work using ffi! + batch.execute('INSERT INTO Test(value) VALUES(?)', ['value1']); await batch.commit(); } Future onOpen(Database db) async { final batch = db.batch(); - batch.rawInsert('INSERT INTO Test(value) VALUES("value2")'); + //batch.rawInsert('INSERT INTO Test(value) VALUES("value2")'); + batch.rawInsert('INSERT INTO Test(value) VALUES(?)', ['value2']); await batch.commit(); } @@ -521,12 +536,14 @@ class OpenTestPage extends TestPage { }); test('Open read-only', () async { + // await Sqflite.devSetDebugModeOn(true); final path = await initDeleteDb('open_read_only.db'); Future onCreate(Database db, int version) async { final batch = db.batch(); batch.execute('CREATE TABLE Test (id INTEGER PRIMARY KEY, value TEXT)'); - batch.rawInsert('INSERT INTO Test(value) VALUES("value1")'); + //batch.rawInsert('INSERT INTO Test(value) VALUES("value1")'); This does not work using ffi + batch.rawInsert('INSERT INTO Test(value) VALUES(?)', ['value1']); await batch.commit(); } @@ -543,7 +560,7 @@ class OpenTestPage extends TestPage { 1); try { - await db.rawInsert('INSERT INTO Test(value) VALUES("value1")'); + await db.rawInsert('INSERT INTO Test(value) VALUES(?)', ['value1']); fail('should fail'); } on DatabaseException catch (e) { // Error DatabaseException(attempt to write a readonly database (code 8)) running Open read-only @@ -558,6 +575,7 @@ class OpenTestPage extends TestPage { }); test('Open demo (doc)', () async { + // await Sqflite.devSetDebugModeOn(true); final path = await initDeleteDb('open_read_only.db'); { @@ -623,10 +641,10 @@ class OpenTestPage extends TestPage { print('Creating new copy from asset'); // Copy from asset - final data = await rootBundle.load(join('assets', 'example.db')); + final data = await rootBundle.load(url.join('assets', 'example.db')); final bytes = data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes); - await File(path).writeAsBytes(bytes); + await writeFileAsBytes(path, bytes); // open the database db = await openDatabase(path, readOnly: true); @@ -639,6 +657,7 @@ class OpenTestPage extends TestPage { }); test('Database locked (doc)', () async { + // await Sqflite.devSetDebugModeOn(true); final path = await initDeleteDb('open_locked.db'); final helper = Helper(path); @@ -652,6 +671,7 @@ class OpenTestPage extends TestPage { }); test('single/multi instance (using factory)', () async { + // await Sqflite.devSetDebugModeOn(true); final path = await initDeleteDb('instances_test.db'); Database? db1, db2, db3; try { @@ -671,6 +691,7 @@ class OpenTestPage extends TestPage { }); test('single/multi instance', () async { + // await Sqflite.devSetDebugModeOn(true); final path = await initDeleteDb('instances_test.db'); final db1 = await openDatabase(path, singleInstance: false); final db2 = await openDatabase(path, singleInstance: true); @@ -708,6 +729,7 @@ class OpenTestPage extends TestPage { }); test('Not in memory database', () async { + // await Sqflite.devSetDebugModeOn(true); final path = await initDeleteDb('not_in_memory.db'); var db = await openDatabase(path); @@ -727,37 +749,41 @@ class OpenTestPage extends TestPage { await db.close(); }); - test('open in sub directory fails', () async { + test('open in sub directory', () async { final databasesPath = await factory.getDatabasesPath(); final path = join(databasesPath, 'sub_that_should_not_exists'); try { - await Directory(path).delete(recursive: true); + await deleteDirectory(path); } catch (_) {} final dbPath = join(path, 'open.db'); try { - await factory.openDatabase(dbPath); + final db = await factory.openDatabase(dbPath); + await db.close(); } catch (e) { - print("couldn't open database in sub directory"); + print("couldn't open database in sub sub directory"); } }); - test('open in sub sub directory fails', () async { + test('open in sub sub directory', () async { + // await Sqflite.devSetDebugModeOn(true); final databasesPath = await factory.getDatabasesPath(); final path = join(databasesPath, 'sub2_that_should_not_exists', 'sub_sub'); try { - await Directory(path).delete(recursive: true); + await deleteDirectory(path); } catch (_) {} - expect(await Directory(path).exists(), false); + expect(await existsDirectory(path), false); final dbPath = join(path, 'open.db'); try { - await factory.openDatabase(dbPath); + final db = await factory.openDatabase(dbPath); + await db.close(); } catch (e) { print("couldn't open database in sub sub directory"); } }); test('open_close_open_no_wait', () async { + // await Sqflite.devSetDebugModeOn(true); const path = 'open_close_open_no_wait.db'; final factory = databaseFactory; await factory.deleteDatabase(path); @@ -779,6 +805,7 @@ class OpenTestPage extends TestPage { } }); test('close in transaction', () async { + // await Sqflite.devSetDebugModeOn(true); const path = 'test_close_in_transaction.db'; final factory = databaseFactory; await factory.deleteDatabase(path); @@ -796,8 +823,9 @@ class OpenTestPage extends TestPage { } }); - test('open in transaction', () async { - const path = 'test_close_in_transaction.db'; + test('Open in transaction', () async { + // await Sqflite.devSetDebugModeOn(true); + const path = 'test_open_in_transaction.db'; final factory = databaseFactory; await factory.deleteDatabase(path); var db = await factory.openDatabase(path, @@ -829,15 +857,16 @@ class OpenTestPage extends TestPage { test('Open non sqlite file', () async { // Kind of corruption simulation + // await Sqflite.devSetDebugModeOn(true); final factory = databaseFactory; final path = join(await factory.getDatabasesPath(), 'test_non_sqlite_file.db'); await factory.deleteDatabase(path); // Write dummy content - await File(path).writeAsString('dummy', flush: true); + await writeFileAsString(path, 'dummy', flush: true); // check content - expect(await File(path).readAsString(), 'dummy'); + expect(await readFileAsString(path), 'dummy'); // try read-only { @@ -856,14 +885,13 @@ class OpenTestPage extends TestPage { await db.close(); // check content - expect(await File(path).readAsString(), 'dummy'); + expect(await readFileAsString(path), 'dummy'); } expect(await isDatabase(path), isFalse); // try read-write const minExpectedSize = 1000; - expect( - (await File(path).readAsBytes()).length, lessThan(minExpectedSize)); + expect((await readFileAsBytes(path)).length, lessThan(minExpectedSize)); var db = await factory.openDatabase(path); try { @@ -877,10 +905,42 @@ class OpenTestPage extends TestPage { db = await factory.openDatabase(path, options: OpenDatabaseOptions(version: 1)); } catch (e) { - print('openDatabase error'); + print('getVersion error'); } await db.close(); }); + test('Read/write bytes', () async { + var path = await initDeleteDb('database_read_bytes.db'); + var writtenPath = await initDeleteDb('database_written_bytes.db'); + var db = await factory.openDatabase(path, + options: OpenDatabaseOptions( + version: 1, + onCreate: (db, version) async { + await db.execute( + 'CREATE TABLE Test(id INTEGER PRIMARY KEY, value TEXT)'); + })); + var textValue = 'value_to_read'; + await db.insert('Test', {'id': 1, 'value': textValue}); + expect(await db.query('Test'), [ + {'id': 1, 'value': textValue} + ]); + await db.close(); + var bytes = await factory.readDatabaseBytes(path); + //expect(bytes.length, 8192); + expect(bytes.sublist(0, 4), [ + 83, + 81, + 76, + 105, + ]); + + await factory.writeDatabaseBytes(writtenPath, bytes); + db = await factory.openDatabase(writtenPath); + expect(await db.query('Test'), [ + {'id': 1, 'value': textValue} + ]); + await db.close(); + }); } } diff --git a/packages/sqflite/example/lib/raw_test_page.dart b/packages/sqflite/example/lib/raw_test_page.dart index 42f9850d1..d02b8fc1b 100644 --- a/packages/sqflite/example/lib/raw_test_page.dart +++ b/packages/sqflite/example/lib/raw_test_page.dart @@ -1,13 +1,11 @@ -import 'dart:async'; -import 'dart:convert'; -import 'dart:io'; +import 'dart:io' as io; import 'package:flutter/foundation.dart'; import 'package:path/path.dart'; import 'package:sqflite/sqflite.dart'; import 'package:sqflite/utils/utils.dart'; -import 'package:sqflite_tizen_example/src/dev_utils.dart'; +import 'src/common_import.dart'; import 'test_page.dart'; // ignore_for_file: avoid_print @@ -17,6 +15,8 @@ class RawTestPage extends TestPage { /// Raw test page. RawTestPage({Key? key}) : super('Raw tests', key: key) { test('Simple', () async { + // await Sqflite.devSetDebugModeOn(true); + final path = await initDeleteDb('raw_simple.db'); final db = await openDatabase(path); try { @@ -44,6 +44,8 @@ class RawTestPage extends TestPage { }); test('Options', () async { + // Sqflite.devSetDebugModeOn(true); + final path = await initDeleteDb('raw_query_format.db'); final db = await openDatabase(path); try { @@ -54,36 +56,9 @@ class RawTestPage extends TestPage { batch.rawInsert('INSERT INTO Test (name) VALUES (?)', ['item 2']); await batch.commit(); - // ignore: deprecated_member_use, deprecated_member_use_from_same_package - var sqfliteOptions = SqfliteOptions()..queryAsMapList = true; - // ignore: deprecated_member_use - await Sqflite.devSetOptions(sqfliteOptions); var sql = 'SELECT id, name FROM Test'; // ignore: deprecated_member_use - var result = await db.devInvokeSqlMethod('query', sql); - var expected = [ - {'id': 1, 'name': 'item 1'}, - {'id': 2, 'name': 'item 2'} - ]; - print('result as map list $result'); - expect(result, expected); - - // empty - sql = 'SELECT id, name FROM Test WHERE id=1234'; - // ignore: deprecated_member_use - result = await db.devInvokeSqlMethod('query', sql); - expected = []; - print('result as map list $result'); - expect(result, expected); - - // ignore: deprecated_member_use, deprecated_member_use_from_same_package - sqfliteOptions = SqfliteOptions()..queryAsMapList = false; - // ignore: deprecated_member_use - await Sqflite.devSetOptions(sqfliteOptions); - - sql = 'SELECT id, name FROM Test'; - // ignore: deprecated_member_use - var resultSet = await db.devInvokeSqlMethod('query', sql); + var resultSet = await db.devInvokeSqlMethod('query', sql); var expectedResultSetMap = { 'columns': ['id', 'name'], 'rows': [ @@ -100,13 +75,24 @@ class RawTestPage extends TestPage { resultSet = await db.devInvokeSqlMethod('query', sql); expectedResultSetMap = {}; print('result as r/c $resultSet'); - expect(resultSet, expectedResultSetMap); + try { + // This might be just for compatibility + expect(resultSet, expectedResultSetMap); + } catch (e) { + // Allow empty result + expectedResultSetMap = { + 'columns': ['id', 'name'], + 'rows': [] + }; + expect(resultSet, expectedResultSetMap); + } } finally { await db.close(); } }); test('Transaction', () async { + //Sqflite.devSetDebugModeOn(true); final path = await initDeleteDb('simple_transaction.db'); final db = await openDatabase(path); try { @@ -117,7 +103,7 @@ class RawTestPage extends TestPage { await db.transaction((txn) async { final count = Sqflite.firstIntValue( await txn.rawQuery('SELECT COUNT(*) FROM Test'))!; - await Future.delayed(const Duration(milliseconds: 40)); + await Future.delayed(const Duration(milliseconds: 40)); await txn .rawInsert('INSERT INTO Test (name) VALUES (?)', ['item $i']); //print(await db.query('SELECT COUNT(*) FROM Test')); @@ -138,12 +124,13 @@ class RawTestPage extends TestPage { }); test('Concurrency 1', () async { + // Sqflite.devSetDebugModeOn(true); final path = await initDeleteDb('simple_concurrency_1.db'); final db = await openDatabase(path); try { - final step1 = Completer(); - final step2 = Completer(); - final step3 = Completer(); + final step1 = Completer(); + final step2 = Completer(); + final step3 = Completer(); Future action1() async { await db @@ -193,12 +180,13 @@ class RawTestPage extends TestPage { }); test('Concurrency 2', () async { + // Sqflite.devSetDebugModeOn(true); final path = await initDeleteDb('simple_concurrency_2.db'); final db = await openDatabase(path); try { - final step1 = Completer(); - final step2 = Completer(); - final step3 = Completer(); + final step1 = Completer(); + final step2 = Completer(); + final step3 = Completer(); Future action1() async { await db @@ -272,6 +260,7 @@ class RawTestPage extends TestPage { }); test('Transaction open twice', () async { + //Sqflite.devSetDebugModeOn(true); final path = await initDeleteDb('transaction_open_twice.db'); final db = await openDatabase(path); Database? db2; @@ -308,6 +297,7 @@ class RawTestPage extends TestPage { }); test('Demo', () async { + // await Sqflite.devSetDebugModeOn(); final path = await initDeleteDb('simple_demo.db'); final database = await openDatabase(path); try { @@ -323,8 +313,11 @@ class RawTestPage extends TestPage { 'CREATE TABLE Test (id INTEGER PRIMARY KEY, name TEXT, value INTEGER, num REAL)'); print('table created'); var id = await database.rawInsert( - 'INSERT INTO Test(name, value, num) VALUES("some name",1234,?)', - [456.789]); + // This does not work using ffi + // 'INSERT INTO Test(name, value, num) VALUES("some name",1234,?)', + // [456.789]); + 'INSERT INTO Test(name, value, num) VALUES(?,1234,?)', + ['some name', 456.789]); print('inserted1: $id'); id = await database.rawInsert( 'INSERT INTO Test(name, value) VALUES(?, ?)', @@ -369,8 +362,8 @@ class RawTestPage extends TestPage { // Make sure the directory exists try { // ignore: avoid_slow_async_io - if (!await Directory(databasesPath).exists()) { - await Directory(databasesPath).create(recursive: true); + if (!await io.Directory(databasesPath).exists()) { + await io.Directory(databasesPath).create(recursive: true); } } catch (_) {} @@ -390,7 +383,9 @@ class RawTestPage extends TestPage { // Insert some records in a transaction await database.transaction((txn) async { final id1 = await txn.rawInsert( - 'INSERT INTO Test(name, value, num) VALUES("some name", 1234, 456.789)'); + // 'INSERT INTO Test(name, value, num) VALUES("some name", 1234, 456.789)'); This does not work using ffi + 'INSERT INTO Test(name, value, num) VALUES(?, 1234, 456.789)', + ['some name']); print('inserted1: $id1'); final id2 = await txn.rawInsert( 'INSERT INTO Test(name, value, num) VALUES(?, ?, ?)', @@ -430,6 +425,7 @@ class RawTestPage extends TestPage { }); test('Open twice', () async { + // Sqflite.devSetDebugModeOn(true); final path = await initDeleteDb('open_twice.db'); final db = await openDatabase(path); Database? db2; @@ -448,6 +444,7 @@ class RawTestPage extends TestPage { }); test('text primary key', () async { + // Sqflite.devSetDebugModeOn(true); final path = await initDeleteDb('text_primary_key.db'); final db = await openDatabase(path); try { @@ -473,10 +470,9 @@ class RawTestPage extends TestPage { } }); - test('without rowid', () async { - // Inserts into WITHOUT ROWID tables are not recorded. If no successful INSERTs - // into rowid tables have ever occurred on the database connection, then returns zero. - // Ref: https://www.sqlite.org/c3ref/last_insert_rowid.html + test('Without rowid', () async { + // Sqflite.devSetDebugModeOn(true); + // this fails on iOS late Database db; try { @@ -488,8 +484,19 @@ class RawTestPage extends TestPage { .execute('CREATE TABLE Test (name TEXT PRIMARY KEY) WITHOUT ROWID'); var id = await db.insert('Test', {'name': 'test'}); expect(id, 0); + id = await db.insert('Test', {'name': 'other'}); expect(id, 0); + + // Insert conflict + // Only tested on Android for now... + try { + id = await db.insert('Test', {'name': 'other'}); + } on DatabaseException catch (e) { + // Test.name (code 1555 SQLITE_CONSTRAINT_PRIMARYKEY)) sql 'INSERT INTO Test (name) VALUES (?)' args [other] running without rowid + expect(e.getResultCode(), 1555); + } + // notice the order is based on the primary key final list = await db.query('Test'); expect(list, [ @@ -532,7 +539,7 @@ class RawTestPage extends TestPage { } }); - test('Binding null', () async { + test('Binding null (fails on Android)', () async { final db = await openDatabase(inMemoryDatabasePath); try { for (var value in [null, 2]) { @@ -545,5 +552,78 @@ class RawTestPage extends TestPage { await db.close(); } }); + + test('Query by page', () async { + // await databaseFactory.debugSetLogLevel(sqfliteLogLevelVerbose); + + //final path = await initDeleteDb('query_by_page.db'); + //final db = await openDatabase(path); + final db = await openDatabase(inMemoryDatabasePath); + try { + await db.execute(''' + CREATE TABLE test ( + id INTEGER PRIMARY KEY + )'''); + await db.insert('test', {'id': 1}); + await db.insert('test', {'id': 2}); + await db.insert('test', {'id': 3}); + var resultsList = []; + + // Use a cursor + var cursor = + await db.rawQueryCursor('SELECT * FROM test', null, bufferSize: 2); + resultsList.clear(); + var results = >[]; + while (await cursor.moveNext()) { + results.add(cursor.current); + } + expect(results, [ + {'id': 1}, + {'id': 2}, + {'id': 3} + ]); + + // Multiple cursors a cursor + var cursor1 = + await db.rawQueryCursor('SELECT * FROM test', null, bufferSize: 2); + var cursor2 = + await db.rawQueryCursor('SELECT * FROM test', null, bufferSize: 1); + await cursor1.moveNext(); + expect(cursor1.current.values, [1]); + await cursor2.moveNext(); + await cursor2.moveNext(); + expect(cursor2.current.values, [2]); + await cursor1.moveNext(); + expect(cursor1.current.values, [2]); + await cursor1.close(); + await cursor1.close(); // ok to call twice + try { + cursor1.current.values; + fail('should fail get current'); + } on StateError catch (_) {} + await cursor2.moveNext(); + expect(cursor2.current.values, [3]); + expect(await cursor2.moveNext(), isFalse); + expect(await cursor1.moveNext(), isFalse); + try { + cursor2.current.values; + fail('should fail get current'); + } on StateError catch (_) {} + + // No data + cursor = await db.rawQueryCursor('SELECT * FROM test WHERE id > ?', [3], + bufferSize: 2); + expect(await cursor.moveNext(), isFalse); + + // Matching page size + cursor = await db.rawQueryCursor('SELECT * FROM test WHERE id > ?', [1], + bufferSize: 2); + expect(await cursor.moveNext(), isTrue); + expect(await cursor.moveNext(), isTrue); + expect(await cursor.moveNext(), isFalse); + } finally { + await db.close(); + } + }); } } diff --git a/packages/sqflite/example/lib/slow_test_page.dart b/packages/sqflite/example/lib/slow_test_page.dart index c3c060229..a1d8a0c3f 100644 --- a/packages/sqflite/example/lib/slow_test_page.dart +++ b/packages/sqflite/example/lib/slow_test_page.dart @@ -1,8 +1,7 @@ -import 'dart:io'; - import 'package:flutter/foundation.dart'; import 'package:sqflite/sqflite.dart'; +import 'src/common_import.dart'; import 'test_page.dart'; // ignore_for_file: avoid_print @@ -79,7 +78,7 @@ class SlowTestPage extends TestPage { await perfDo(count); }); - if (Platform.isAndroid) { + if (platform.isAndroid) { test('Perf android NORMAL_PRIORITY', () async { // ignore_for_file: deprecated_member_use, deprecated_member_use_from_same_package await Sqflite.devSetOptions( @@ -147,4 +146,8 @@ class SlowTestPage extends TestPage { print('1000 insert ${sw.elapsed}'); await db.close(); } + +// 2019-02-26 + +// BACKGROUND } diff --git a/packages/sqflite/example/lib/src/common_import.dart b/packages/sqflite/example/lib/src/common_import.dart index edafc0cf4..9637df3b1 100644 --- a/packages/sqflite/example/lib/src/common_import.dart +++ b/packages/sqflite/example/lib/src/common_import.dart @@ -2,5 +2,7 @@ export 'dart:async'; export 'dart:convert'; export 'package:collection/collection.dart'; +export 'package:sqflite_common/src/internals.dart'; +export 'package:sqflite_common/src/platform/platform.dart'; export 'dev_utils.dart'; diff --git a/packages/sqflite/example/lib/src/expect.dart b/packages/sqflite/example/lib/src/expect.dart index a4d2ffd86..46c9e8d20 100644 --- a/packages/sqflite/example/lib/src/expect.dart +++ b/packages/sqflite/example/lib/src/expect.dart @@ -49,10 +49,10 @@ typedef ErrorFormatter = String Function(dynamic actual, Matcher matcher, /// you want to wait for the matcher to complete before continuing the test, you /// can call [expectLater] instead and `await` the result. void expect( - actual, - matcher, { + Object? actual, + Object? matcher, { String? reason, - skip, + Object? skip, }) { _expect(actual, matcher, reason: reason, skip: skip); } @@ -68,10 +68,11 @@ void expect( /// /// If the matcher fails asynchronously, that failure is piped to the returned /// future where it can be handled by user code. -Future expectLater(actual, matcher, {String? reason, skip}) => +Future expectLater(Object? actual, Object? matcher, + {String? reason, Object? skip}) => _expect(actual, matcher, reason: reason, skip: skip) as Future; -String _formatFailure(Matcher expected, actual, String which, +String _formatFailure(Matcher expected, Object? actual, String which, {String? reason}) { final buffer = StringBuffer(); buffer.writeln(indent(prettyPrint(expected), first: 'Expected: ')); @@ -82,9 +83,9 @@ String _formatFailure(Matcher expected, actual, String which, } /// The implementation of [expect] and [expectLater]. -FutureOr _expect(actual, matcher, +FutureOr _expect(Object? actual, Object? matcher, {String? reason, - skip, + Object? skip, bool verbose = false, // ignore: deprecated_member_use, deprecated_member_use_from_same_package ErrorFormatter? formatter}) { @@ -103,7 +104,7 @@ FutureOr _expect(actual, matcher, matcher = wrapMatcher(matcher); - final matchState = {}; + final matchState = {}; try { if ((matcher as Matcher).matches(actual, matchState)) { return Future.sync(() {}); @@ -116,7 +117,7 @@ FutureOr _expect(actual, matcher, /// Convenience method for throwing a new [TestFailure] with the provided /// [message]. -void fail([String? message]) => throw TestFailure(message ?? 'should fail'); +Never fail([String? message]) => throw TestFailure(message ?? 'should fail'); /// index text helper. String indent(String text, {String? first}) { @@ -136,7 +137,8 @@ String prettyPrint(dynamic text, {String? first}) { /// The default error formatter. @Deprecated('Will be removed in 0.13.0.') -String formatFailure(Matcher expected, actual, String which, {String? reason}) { +String formatFailure(Matcher expected, Object? actual, String which, + {String? reason}) { final buffer = StringBuffer(); buffer.writeln(indent(prettyPrint(expected), first: 'Expected: ')); buffer.writeln(indent(prettyPrint(actual), first: ' Actual: ')); diff --git a/packages/sqflite/example/lib/src/item_widget.dart b/packages/sqflite/example/lib/src/item_widget.dart index 80c4f601c..f263690f4 100644 --- a/packages/sqflite/example/lib/src/item_widget.dart +++ b/packages/sqflite/example/lib/src/item_widget.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; - -import '../model/item.dart'; +import 'package:sqflite_tizen_example/model/item.dart'; /// Item widget. class ItemWidget extends StatefulWidget { @@ -15,7 +14,7 @@ class ItemWidget extends StatefulWidget { final Item item; /// Action when pressed (typically run). - final Function(Item item) onTap; // = Function(MainItem item); + final void Function(Item item) onTap; // = Function(MainItem item); @override // ignore: library_private_types_in_public_api diff --git a/packages/sqflite/example/lib/src/main_item_widget.dart b/packages/sqflite/example/lib/src/main_item_widget.dart index 03696db7d..26fbf8146 100644 --- a/packages/sqflite/example/lib/src/main_item_widget.dart +++ b/packages/sqflite/example/lib/src/main_item_widget.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; - -import '../model/main_item.dart'; +import 'package:sqflite_tizen_example/model/main_item.dart'; /// Main item widget. class MainItemWidget extends StatefulWidget { @@ -11,7 +10,7 @@ class MainItemWidget extends StatefulWidget { final MainItem item; /// onTap action (typically run or open). - final Function(MainItem item) onTap; // = Function(MainItem item); + final void Function(MainItem item) onTap; // = Function(MainItem item); @override // ignore: library_private_types_in_public_api diff --git a/packages/sqflite/example/lib/test_page.dart b/packages/sqflite/example/lib/test_page.dart index 2a056e801..ecc11f45d 100644 --- a/packages/sqflite/example/lib/test_page.dart +++ b/packages/sqflite/example/lib/test_page.dart @@ -12,7 +12,6 @@ export 'package:matcher/matcher.dart'; export 'package:sqflite_tizen_example/database/database.dart'; export 'src/expect.dart' show expect, fail; - // ignore_for_file: avoid_print /// Base test page. @@ -71,7 +70,7 @@ bool? verify(bool? condition, [String? message]) { } /// Group. -abstract class Group { +abstract mixin class Group { /// List of tests. List get tests; diff --git a/packages/sqflite/example/lib/type_test_page.dart b/packages/sqflite/example/lib/type_test_page.dart index 60c3dd385..65546d37c 100644 --- a/packages/sqflite/example/lib/type_test_page.dart +++ b/packages/sqflite/example/lib/type_test_page.dart @@ -3,6 +3,7 @@ import 'dart:math'; import 'package:flutter/foundation.dart'; import 'package:sqflite/sqflite.dart'; +import 'package:sqflite_tizen_example/utils.dart'; import 'test_page.dart'; @@ -50,10 +51,10 @@ class TypeTestPage extends TestPage { id = await insertValue(value); //devPrint('${value} ${await getValue(id)}'); expect(await getValue(id), value, reason: '$value ${await getValue(id)}'); + /* id = await insertValue(pow(2, 63)); - // devPrint('2^63: ${pow(2, 63)} ${await getValue(id)}'); - assert(await getValue(id) == pow(2, 63), - '2^63: ${pow(2, 63)} ${await getValue(id)}'); + devPrint('2^63: ${pow(2, 63)} ${await getValue(id)}'); + assert(await getValue(id) == pow(2, 63), '2^63: ${pow(2, 63)} ${await getValue(id)}'); // more then 64 bits id = await insertValue(pow(2, 65)); @@ -62,6 +63,7 @@ class TypeTestPage extends TestPage { // more then 128 bits id = await insertValue(pow(2, 129)); assert(await getValue(id) == pow(2, 129)); + */ await data.db.close(); }); @@ -141,11 +143,14 @@ class TypeTestPage extends TestPage { //print(await getValue(id)); //assert(eq.equals(await getValue(id), [])); - final blob1234 = [1, 2, 3, 4]; + var blob1234 = [1, 2, 3, 4]; + if (!supportsCompatMode) { + blob1234 = Uint8List.fromList(blob1234); + } id = await insertValue(blob1234); - final value = (await getValue(id)) as List; + dynamic value = (await getValue(id)) as List; print(value); - print('${value.length}'); + print('${(value as List).length}'); expect(value, blob1234, reason: '${await getValue(id)}'); // test hex feature on sqlite @@ -153,12 +158,12 @@ class TypeTestPage extends TestPage { .rawQuery('SELECT hex(value) FROM Test WHERE id = ?', [id]); expect(hexResult[0].values.first, '01020304'); - // try blob lookup + // try blob lookup (works on Android since 2022-09-19) var rows = await data.db .rawQuery('SELECT * FROM Test WHERE value = ?', [blob1234]); expect(rows.length, 1); - // // try blob lookup using hex + // try blob lookup using hex rows = await data.db.rawQuery( 'SELECT * FROM Test WHERE hex(value) = ?', [Sqflite.hex(blob1234)]); expect(rows.length, 1); @@ -169,7 +174,6 @@ class TypeTestPage extends TestPage { // final blobEmpty = Uint8List(0); // id = await insertValue(blobEmpty); // value = await getValue(id); - // print(value); // expect(value, const TypeMatcher()); // expect(value, isEmpty); } finally { @@ -235,7 +239,10 @@ class TypeTestPage extends TestPage { } on ArgumentError catch (_) { failed = true; } - expect(failed, isFalse); + if (supportsCompatMode) { + print('for now bool are accepted but inconsistent on iOS/Android'); + expect(failed, isFalse); + } } finally { await data.db.close(); } diff --git a/packages/sqflite/example/lib/utils.dart b/packages/sqflite/example/lib/utils.dart index de4d5650d..cf8cfc7c6 100644 --- a/packages/sqflite/example/lib/utils.dart +++ b/packages/sqflite/example/lib/utils.dart @@ -1,5 +1,12 @@ -export 'dart:io' hide sleep; +import 'package:sqflite/sqflite.dart'; +import 'package:sqflite/sqflite_dev.dart'; /// Usage: await sleep(500); Future sleep([int milliseconds = 0]) => - Future.delayed(Duration(milliseconds: milliseconds)); + Future.delayed(Duration(milliseconds: milliseconds)); + +/// Supports compat mode (devSetDebugModeOn, queryAsMap, fts4, some error handled - missing parameter, bad file) +bool get supportsCompatMode { + // ignore: invalid_use_of_visible_for_testing_member + return databaseFactory == sqfliteDatabaseFactoryDefault; +} diff --git a/packages/sqflite/example/pubspec.yaml b/packages/sqflite/example/pubspec.yaml index 805cdda75..c9b9e47cc 100644 --- a/packages/sqflite/example/pubspec.yaml +++ b/packages/sqflite/example/pubspec.yaml @@ -3,7 +3,7 @@ description: Demonstrates how to use the sqflite_tizen plugin. publish_to: "none" environment: - sdk: ">=2.18.0 <4.0.0" + sdk: ">=3.0.0 <4.0.0" flutter: ">=3.3.0" dependencies: @@ -11,7 +11,7 @@ dependencies: collection: any flutter: sdk: flutter - sqflite: ^2.2.8 + sqflite: ^2.3.0 sqflite_common: sqflite_tizen: path: ../ diff --git a/packages/sqflite/pubspec.yaml b/packages/sqflite/pubspec.yaml index 4abdca4e0..e3c2db26d 100644 --- a/packages/sqflite/pubspec.yaml +++ b/packages/sqflite/pubspec.yaml @@ -2,10 +2,10 @@ name: sqflite_tizen description: Tizen implementation of the sqflite plugin. homepage: https://github.com/flutter-tizen/plugins repository: https://github.com/flutter-tizen/plugins/tree/master/packages/sqflite -version: 0.1.2 +version: 0.1.3 environment: - sdk: ">=2.18.0 <4.0.0" + sdk: ">=3.0.0 <4.0.0" flutter: ">=3.3.0" flutter: @@ -19,7 +19,7 @@ flutter: dependencies: flutter: sdk: flutter - sqflite: ^2.2.8 + sqflite: ^2.3.0 dev_dependencies: flutter_lints: ^1.0.4