From a8cfcd02ce8fd71289971513e744897f6f857bbe Mon Sep 17 00:00:00 2001 From: monsieurtanuki Date: Sun, 20 Oct 2024 14:17:21 +0200 Subject: [PATCH] fix: 5693 - able to run tasks without minimum duration wait (#5694) * fix: 5693 - able to run tasks without minimum duration wait Impacted files: * `background_task_manager.dart`: made it possible to force an immediate run if possible * `offline_tasks_page.dart`: force an immediate run if possible when clicking on the refresh button * Added a call to BackgroundTaskManager.run related to prices * Minor fix * Wtf tests * Ugly fix for mocking hive --- .../background/background_task_manager.dart | 54 +++++++++++++------ .../lib/database/local_database.dart | 7 +++ .../lib/pages/offline_tasks_page.dart | 4 +- .../preferences/user_preferences_page.dart | 4 ++ .../dialogs/generic_lib/dialogs_test.dart | 2 + .../test/tests_utils/local_database_mock.dart | 9 ++++ .../forgot_password_page_layout_test.dart | 2 + .../test/users/login_page_layout_test.dart | 2 + .../test/users/signup_page_layout_test.dart | 2 + 9 files changed, 68 insertions(+), 18 deletions(-) diff --git a/packages/smooth_app/lib/background/background_task_manager.dart b/packages/smooth_app/lib/background/background_task_manager.dart index 66704385daf..2ca1815d627 100644 --- a/packages/smooth_app/lib/background/background_task_manager.dart +++ b/packages/smooth_app/lib/background/background_task_manager.dart @@ -40,7 +40,7 @@ class BackgroundTaskManager { ); await DaoStringList(localDatabase).add(DaoStringList.keyTasks, taskId); await task.preExecute(localDatabase); - run(); + run(forceNowIfPossible: true); } /// Finishes a task cleanly. @@ -103,11 +103,13 @@ class BackgroundTaskManager { static const int _minimumDurationBetweenRuns = 5 * 1000; /// Returns the "now" timestamp if we can run now, or `null`. - int? _canStartNow() { - final DaoInt daoInt = DaoInt(localDatabase); + /// + /// With [forceNowIfPossible] we can be more aggressive and force the decision + /// of running now or at least just after the current running block. + int? _canStartNow(final bool forceNowIfPossible) { final int now = LocalDatabase.nowInMillis(); - final int? latestRunStart = daoInt.get(_lastStartTimestampKey); - final int? latestRunStop = daoInt.get(_lastStopTimestampKey); + final int? latestRunStart = localDatabase.daoIntGet(_lastStartTimestampKey); + final int? latestRunStop = localDatabase.daoIntGet(_lastStopTimestampKey); if (_running) { // if pretending to be running but started a very very long time ago if (latestRunStart != null && @@ -115,6 +117,10 @@ class BackgroundTaskManager { // we assume we can run now. return now; } + // let's try again at the end of the current run. + if (forceNowIfPossible) { + _forceRunAgain = true; + } return null; } // if the last run stopped correctly or was started a long time ago. @@ -123,7 +129,10 @@ class BackgroundTaskManager { // if the last run stopped not enough time ago. if (latestRunStop != null && latestRunStop + _minimumDurationBetweenRuns >= now) { - return null; + // let's apply that minimum duration if there's no rush + if (!forceNowIfPossible) { + return null; + } } return now; } @@ -132,8 +141,8 @@ class BackgroundTaskManager { /// Signals we've just finished working and that we're ready for a new run. Future _justFinished() async { - await DaoInt(localDatabase).put(_lastStartTimestampKey, null); - await DaoInt(localDatabase).put( + await localDatabase.daoIntPut(_lastStartTimestampKey, null); + await localDatabase.daoIntPut( _lastStopTimestampKey, LocalDatabase.nowInMillis(), ); @@ -141,11 +150,18 @@ class BackgroundTaskManager { bool _running = false; + /// Flag to say: I know you're running, please try again, it's worth it. + bool _forceRunAgain = false; + /// Runs all the pending tasks, and then smoothly ends, without awaiting. - void run() { - // no await - _runAsync(); - } + /// + /// Can be called in 2 cases: + /// 1. we've just created a task and we really want it to be executed ASAP + /// `forceNowIfPossible = true` + /// 2. we're just checking casually if there are pending tasks + /// `forceNowIfPossible = false` + void run({final bool forceNowIfPossible = false}) => + unawaited(_runAsync(forceNowIfPossible)); /// Runs all the pending tasks, and then smoothly ends. /// @@ -154,19 +170,17 @@ class BackgroundTaskManager { /// If a task fails and another task with the same stamp comes after, /// we can remove the failed task from the list: it would have been /// overwritten anyway. - Future _runAsync() async { - final int? now = _canStartNow(); + Future _runAsync(final bool forceNowIfPossible) async { + final int? now = _canStartNow(forceNowIfPossible); if (now == null) { return; } _running = true; - /// /// Will also set the "latest start timestamp". /// With this, we can detect a run that went wrong. /// Like, still running 1 hour later. - final DaoInt daoInt = DaoInt(localDatabase); - await daoInt.put(_lastStartTimestampKey, now); + await localDatabase.daoIntPut(_lastStartTimestampKey, now); bool runAgain = true; while (runAgain) { runAgain = false; @@ -196,6 +210,12 @@ class BackgroundTaskManager { } } await _justFinished(); + if (!runAgain) { + if (_forceRunAgain) { + runAgain = true; + _forceRunAgain = false; + } + } } _running = false; } diff --git a/packages/smooth_app/lib/database/local_database.dart b/packages/smooth_app/lib/database/local_database.dart index 5d5d7abddda..7969c8145c1 100644 --- a/packages/smooth_app/lib/database/local_database.dart +++ b/packages/smooth_app/lib/database/local_database.dart @@ -51,6 +51,13 @@ class LocalDatabase extends ChangeNotifier { List getAllTaskIds() => DaoStringList(this).getAll(DaoStringList.keyTasks); + /// Ugly solution to be able to mock hive data. + int? daoIntGet(final String key) => DaoInt(this).get(key); + + /// Ugly solution to be able to mock hive data. + Future daoIntPut(final String key, final int? value) => + DaoInt(this).put(key, value); + static Future getLocalDatabase() async { // sql from there String? databasesRootPath; diff --git a/packages/smooth_app/lib/pages/offline_tasks_page.dart b/packages/smooth_app/lib/pages/offline_tasks_page.dart index 3758922aae7..327598cd0b1 100644 --- a/packages/smooth_app/lib/pages/offline_tasks_page.dart +++ b/packages/smooth_app/lib/pages/offline_tasks_page.dart @@ -32,7 +32,9 @@ class _OfflineTaskState extends State { actions: [ IconButton( onPressed: () => - BackgroundTaskManager.getInstance(localDatabase).run(), + BackgroundTaskManager.getInstance(localDatabase).run( + forceNowIfPossible: true, + ), icon: const Icon(Icons.refresh), ), ], diff --git a/packages/smooth_app/lib/pages/preferences/user_preferences_page.dart b/packages/smooth_app/lib/pages/preferences/user_preferences_page.dart index 1aae09487a3..17c6ab20c79 100644 --- a/packages/smooth_app/lib/pages/preferences/user_preferences_page.dart +++ b/packages/smooth_app/lib/pages/preferences/user_preferences_page.dart @@ -5,9 +5,11 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:matomo_tracker/matomo_tracker.dart'; import 'package:provider/provider.dart'; +import 'package:smooth_app/background/background_task_manager.dart'; import 'package:smooth_app/data_models/preferences/user_preferences.dart'; import 'package:smooth_app/data_models/product_preferences.dart'; import 'package:smooth_app/data_models/user_management_provider.dart'; +import 'package:smooth_app/database/local_database.dart'; import 'package:smooth_app/generic_lib/design_constants.dart'; import 'package:smooth_app/generic_lib/widgets/smooth_back_button.dart'; import 'package:smooth_app/helpers/app_helper.dart'; @@ -44,6 +46,8 @@ enum PreferencePageType { required final UserPreferences userPreferences, required final BuildContext context, }) { + final LocalDatabase localDatabase = context.read(); + BackgroundTaskManager.getInstance(localDatabase).run(); final AppLocalizations appLocalizations = AppLocalizations.of(context); final ThemeProvider themeProvider = context.read(); final ThemeData themeData = Theme.of(context); diff --git a/packages/smooth_app/test/dialogs/generic_lib/dialogs_test.dart b/packages/smooth_app/test/dialogs/generic_lib/dialogs_test.dart index 7d56d66a46f..526c55a091a 100644 --- a/packages/smooth_app/test/dialogs/generic_lib/dialogs_test.dart +++ b/packages/smooth_app/test/dialogs/generic_lib/dialogs_test.dart @@ -14,6 +14,7 @@ import 'package:smooth_app/themes/contrast_provider.dart'; import 'package:smooth_app/themes/theme_provider.dart'; import '../../tests_utils/goldens.dart'; +import '../../tests_utils/local_database_mock.dart'; import '../../tests_utils/mocks.dart'; void main() { @@ -72,6 +73,7 @@ void main() { const UserPreferencesPage( type: PreferencePageType.CONTRIBUTE, ), + localDatabase: MockLocalDatabase(), ), ); await tester.pumpAndSettle(); diff --git a/packages/smooth_app/test/tests_utils/local_database_mock.dart b/packages/smooth_app/test/tests_utils/local_database_mock.dart index ef0de9bb4ac..3ae0aa6edcd 100644 --- a/packages/smooth_app/test/tests_utils/local_database_mock.dart +++ b/packages/smooth_app/test/tests_utils/local_database_mock.dart @@ -2,6 +2,15 @@ import 'package:mockito/mockito.dart'; import 'package:smooth_app/database/local_database.dart'; class MockLocalDatabase extends Mock implements LocalDatabase { + final Map _daoInt = {}; + @override List getAllTaskIds() => []; + + @override + int? daoIntGet(final String key) => _daoInt[key]; + + @override + Future daoIntPut(final String key, final int? value) async => + _daoInt[key] = value; } diff --git a/packages/smooth_app/test/users/forgot_password_page_layout_test.dart b/packages/smooth_app/test/users/forgot_password_page_layout_test.dart index a626e8c30d1..d28891bf296 100644 --- a/packages/smooth_app/test/users/forgot_password_page_layout_test.dart +++ b/packages/smooth_app/test/users/forgot_password_page_layout_test.dart @@ -11,6 +11,7 @@ import 'package:smooth_app/themes/contrast_provider.dart'; import 'package:smooth_app/themes/theme_provider.dart'; import '../tests_utils/goldens.dart'; +import '../tests_utils/local_database_mock.dart'; import '../tests_utils/mocks.dart'; void main() { @@ -51,6 +52,7 @@ void main() { textContrastProvider, colorProvider, const ForgotPasswordPage(), + localDatabase: MockLocalDatabase(), ), ); await tester.pump(); diff --git a/packages/smooth_app/test/users/login_page_layout_test.dart b/packages/smooth_app/test/users/login_page_layout_test.dart index fc372dae573..7719b164caa 100644 --- a/packages/smooth_app/test/users/login_page_layout_test.dart +++ b/packages/smooth_app/test/users/login_page_layout_test.dart @@ -11,6 +11,7 @@ import 'package:smooth_app/themes/contrast_provider.dart'; import 'package:smooth_app/themes/theme_provider.dart'; import '../tests_utils/goldens.dart'; +import '../tests_utils/local_database_mock.dart'; import '../tests_utils/mocks.dart'; void main() { @@ -51,6 +52,7 @@ void main() { textContrastProvider, colorProvider, const LoginPage(), + localDatabase: MockLocalDatabase(), ), ); await tester.pump(); diff --git a/packages/smooth_app/test/users/signup_page_layout_test.dart b/packages/smooth_app/test/users/signup_page_layout_test.dart index 24246e34560..fa91ca43728 100644 --- a/packages/smooth_app/test/users/signup_page_layout_test.dart +++ b/packages/smooth_app/test/users/signup_page_layout_test.dart @@ -11,6 +11,7 @@ import 'package:smooth_app/themes/contrast_provider.dart'; import 'package:smooth_app/themes/theme_provider.dart'; import '../tests_utils/goldens.dart'; +import '../tests_utils/local_database_mock.dart'; import '../tests_utils/mocks.dart'; void main() { @@ -51,6 +52,7 @@ void main() { textContrastProvider, colorProvider, const SignUpPage(), + localDatabase: MockLocalDatabase(), ), ); await tester.pump();