From edec0d4f7b4aa3955c1edcd62e5bf529967b9c72 Mon Sep 17 00:00:00 2001 From: Iiro Krankka Date: Sat, 25 May 2019 18:46:37 +0200 Subject: [PATCH] Fix a bug where only the first listener would receive value updates. --- lib/src/preference/preference.dart | 34 +++++++-------- test/preference/preference_test.dart | 65 ++++++++++++++++++++-------- 2 files changed, 63 insertions(+), 36 deletions(-) diff --git a/lib/src/preference/preference.dart b/lib/src/preference/preference.dart index 01d58bc..2e44524 100755 --- a/lib/src/preference/preference.dart +++ b/lib/src/preference/preference.dart @@ -111,17 +111,6 @@ class _EmitValueChanges extends StreamTransformerBase { final PreferenceAdapter valueAdapter; final SharedPreferences preferences; - T _lastValue; - - void _addIfChanged(StreamController controller, T value) { - // If the value changed from the last one, emit - // it and update "lastValue" with the newest one. - if (value != _lastValue) { - controller.add(value); - _lastValue = value; - } - } - T _getValueFromPersistentStorage() { // Return the latest value from preferences, // If null, returns the default value. @@ -139,7 +128,12 @@ class _EmitValueChanges extends StreamTransformerBase { onListen: () { // When the stream is listened to, start with the current persisted // value. - _addIfChanged(controller, _getValueFromPersistentStorage()); + final value = _getValueFromPersistentStorage(); + controller.add(value); + + // Cache the last value. Caching is specific for each listener, so the + // cached value exists inside the onListen() callback for a reason. + T lastValue = value; // Whenever a key has been updated, fetch the current persisted value // and emit it. @@ -147,16 +141,18 @@ class _EmitValueChanges extends StreamTransformerBase { .transform(_EmitOnlyMatchingKeys(key)) .map((_) => _getValueFromPersistentStorage()) .listen( - (value) => _addIfChanged(controller, value), - onDone: controller.close, - ); + (value) { + if (value != lastValue) { + controller.add(value); + lastValue = value; + } + }, + onDone: () => controller.close(), + ); }, onPause: ([resumeSignal]) => subscription.pause(resumeSignal), onResume: () => subscription.resume(), - onCancel: () { - _lastValue = null; - return subscription.cancel(); - }, + onCancel: () => subscription.cancel(), ); return controller.stream.listen(null); diff --git a/test/preference/preference_test.dart b/test/preference/preference_test.dart index 659cbe2..ebee8c4 100755 --- a/test/preference/preference_test.dart +++ b/test/preference/preference_test.dart @@ -30,6 +30,14 @@ void main() { ); }); + Future _updateValue(String newValue) async { + when(preferences.getString('key')).thenReturn(newValue); + + // The value passed to setValue does not matter in tests - it just merely + // tells the preference that something just changed. + await preference.setValue(null); + } + test('calling setValue() calls the correct key and emits key updates', () { preference.setValue('value1'); preference.setValue('value2'); @@ -75,34 +83,57 @@ void main() { expect(preference, emits('3')); }); - test('does not emit same value more than once in a row', () async { + test('does not emit same value more than once in a row for one listener', + () async { int updateCount = 0; preference.listen((_) => updateCount++); - when(preferences.getString('key')).thenReturn('new value'); - await preference.setValue(null); - - when(preferences.getString('key')).thenReturn('new value'); - await preference.setValue(null); - - when(preferences.getString('key')).thenReturn('new value'); - await preference.setValue(null); + await _updateValue('new value'); + await _updateValue('new value'); + await _updateValue('new value'); // Changed from "default value" to "new value" expect(updateCount, 2); - when(preferences.getString('key')).thenReturn('another value 1'); - await preference.setValue(null); - - when(preferences.getString('key')).thenReturn('another value 2'); - await preference.setValue(null); - - when(preferences.getString('key')).thenReturn('another value 3'); - await preference.setValue(null); + await _updateValue('another value 1'); + await _updateValue('another value 2'); + await _updateValue('another value 3'); // Changed from "new value" to "another value" 3 times expect(updateCount, 5); }); + + test('emits each value change to all listeners', () async { + String value1; + String value2; + String value3; + + preference.listen((value) => value1 = value); + preference.listen((value) => value2 = value); + preference.listen((value) => value3 = value); + + await _updateValue(null); + + expect(value1, 'default value'); + expect(value2, 'default value'); + expect(value3, 'default value'); + + // The value passed to setValue does not matter in tests - it just merely + // triggers the preference that something just changed. + await _updateValue('first change'); + + expect(value1, 'first change'); + expect(value2, 'first change'); + expect(value3, 'first change'); + + // The value passed to setValue does not matter in tests - it just merely + // triggers the preference that something just changed. + await _updateValue('second change'); + + expect(value1, 'second change'); + expect(value2, 'second change'); + expect(value3, 'second change'); + }); }); }