Integration of Storage API to flutter - Users, Units 72/30172/8
authorLudwig Schwiedrzik <ludwig.schwiedrzik@d-fine.com>
Wed, 14 Aug 2024 09:50:33 +0000 (11:50 +0200)
committerScott Murray <scott.murray@konsulko.com>
Thu, 12 Sep 2024 16:58:42 +0000 (16:58 +0000)
Updated user and units notifiers to make use of persistent storage. New
users are stored with a name and id in the default namespace, and their
unit preferences (distance, temperature, tire pressure) are stored in
namespaces corresponding to each user's id.

Added new initialize_settings to load user and unit settings from
storage on startup. For first-time startup, default users with ids have
been added to storage as well.

Added unit tests for new user and unit handling.

Bug-AGL: [SPEC-5228]
Change-Id: I9cbcfc3ea5045dcb27db1b05e332f89abfc284a1
Signed-off-by: Tom Kronsbein <tom.kronsbein@d-fine.com>
Signed-off-by: Ludwig Schwiedrzik <ludwig.schwiedrzik@d-fine.com>
lib/core/constants/users_path.dart [new file with mode: 0644]
lib/data/data_providers/app.dart
lib/data/data_providers/initialize_settings.dart [new file with mode: 0644]
lib/data/data_providers/units_notifier.dart
lib/data/data_providers/users_notifier.dart
lib/data/models/users.dart
lib/export.dart
lib/main.dart
test/storageAPI_UnitsForUsers_test.dart [new file with mode: 0644]

diff --git a/lib/core/constants/users_path.dart b/lib/core/constants/users_path.dart
new file mode 100644 (file)
index 0000000..47c2486
--- /dev/null
@@ -0,0 +1,6 @@
+class UsersPath {
+  static const String InfotainmentCurrentUser =
+      'Infotainment.Users.selectedUser';
+  static const String InfotainmentUsers =
+      'Infotainment.Users';
+}
index 3368a83..05e56f0 100644 (file)
@@ -12,13 +12,11 @@ class App extends StatelessWidget {
 
   @override
   Widget build(BuildContext context) {
-    return ProviderScope(
-      child: MaterialApp(
+    return MaterialApp(
         debugShowCheckedModeBanner: false,
         theme: theme,
         home: const AppView(),
-      ),
-    );
+      );
   }
 }
 
diff --git a/lib/data/data_providers/initialize_settings.dart b/lib/data/data_providers/initialize_settings.dart
new file mode 100644 (file)
index 0000000..b5a5e80
--- /dev/null
@@ -0,0 +1,12 @@
+import 'package:flutter_ics_homescreen/export.dart';
+
+Future<void> initializeSettings(ProviderContainer container) async {
+  await container.read(usersProvider.notifier).loadSettingsUsers();
+  await container.read(unitStateProvider.notifier).loadSettingsUnits();
+  // Initialize other settings or providers if needed.
+}
+
+Future<void> initializeSettingsUser(Ref ref) async {
+  await ref.read(unitStateProvider.notifier).loadSettingsUnits();
+  // Initialize other settings or providers if needed.
+}
index 68c9e65..daf9c92 100644 (file)
@@ -1,12 +1,53 @@
 import 'package:flutter_ics_homescreen/export.dart';
 import 'package:protos/val_api.dart';
 
+import 'package:protos/storage-api.dart' as storage_api;
+import 'initialize_settings.dart';
+
 class UnitsNotifier extends Notifier<Units> {
   @override
   Units build() {
     return const Units.initial();
   }
 
+  // Load Units state of the selected user from the storage API.
+  Future <void> loadSettingsUnits() async {
+    final storageClient = ref.read(storageClientProvider);
+    final userClient = ref.read(usersProvider);
+    
+    try {
+      await initializeSettingsUser(ref);
+    } catch (e) {
+      print('Error loading settings of user: $e');
+    }
+    
+    try {
+      // Read unit values from the selected user namespace.
+      final distanceResponse = await storageClient.read(storage_api.Key(key: VSSPath.vehicleHmiDistanceUnit, namespace: userClient.selectedUser.id));
+      final temperatureResponse = await storageClient.read(storage_api.Key(key: VSSPath.vehicleHmiTemperatureUnit, namespace: userClient.selectedUser.id));
+      final pressureResponse = await storageClient.read(storage_api.Key(key: VSSPath.vehicleHmiPressureUnit, namespace: userClient.selectedUser.id));
+
+      // Prepare state declaration and fall back to default values if the key is not present in the storage API.
+      final distanceUnit = distanceResponse.result == 'MILES'
+          ? DistanceUnit.miles
+          : DistanceUnit.kilometers;
+
+      final temperatureUnit = temperatureResponse.result == 'F'
+          ? TemperatureUnit.fahrenheit
+          : TemperatureUnit.celsius;
+
+      final pressureUnit = pressureResponse.result == 'PSI'
+          ? PressureUnit.psi
+          : PressureUnit.kilopascals;
+
+      state =  Units(distanceUnit, temperatureUnit, pressureUnit);
+    } catch (e) {
+      // Fallback to initial defaults if error occurs.
+      print('Error loading settings for units: $e');
+      state = const Units.initial();
+    }
+  }
+
   bool handleSignalUpdate(DataEntry entry) {
     bool handled = true;
     switch (entry.path) {
@@ -40,7 +81,7 @@ class UnitsNotifier extends Notifier<Units> {
     return handled;
   }
 
-  void setDistanceUnit(DistanceUnit unit) {
+  Future <void> setDistanceUnit(DistanceUnit unit) async {
     state = state.copyWith(distanceUnit: unit);
     var valClient = ref.read(valClientProvider);
     valClient.setString(
@@ -48,9 +89,21 @@ class UnitsNotifier extends Notifier<Units> {
       unit == DistanceUnit.kilometers ? "KILOMETERS" : "MILES",
       true,
     );
+    // Write to storage API (to selected user namespace).
+    var storageClient = ref.read(storageClientProvider);
+    final userClient = ref.read(usersProvider);
+    try {
+      await storageClient.write(storage_api.KeyValue(
+        key: VSSPath.vehicleHmiDistanceUnit,
+        value: unit == DistanceUnit.kilometers ? 'KILOMETERS' : 'MILES',
+        namespace: userClient.selectedUser.id
+      ));
+    } catch (e) {
+      print('Error saving distance unit: $e');
+    }
   }
 
-  void setTemperatureUnit(TemperatureUnit unit) {
+  Future <void> setTemperatureUnit(TemperatureUnit unit) async {
     state = state.copyWith(temperatureUnit: unit);
     var valClient = ref.read(valClientProvider);
     valClient.setString(
@@ -58,9 +111,21 @@ class UnitsNotifier extends Notifier<Units> {
       unit == TemperatureUnit.celsius ? "C" : "F",
       true,
     );
+    // Write to storage API (to selected user namespace).
+    var storageClient = ref.read(storageClientProvider);
+    final userClient = ref.read(usersProvider);
+    try {
+      await storageClient.write(storage_api.KeyValue(
+        key: VSSPath.vehicleHmiTemperatureUnit,
+        value: unit == TemperatureUnit.celsius ? "C" : "F",
+        namespace: userClient.selectedUser.id
+      ));
+    } catch (e) {
+      print('Error saving distance unit: $e');
+    }
   }
 
-  void setPressureUnit(PressureUnit unit) {
+  Future <void> setPressureUnit(PressureUnit unit) async {
     state = state.copyWith(pressureUnit: unit);
     var valClient = ref.read(valClientProvider);
     valClient.setString(
@@ -68,5 +133,17 @@ class UnitsNotifier extends Notifier<Units> {
       unit == PressureUnit.kilopascals ? "KPA" : "PSI",
       true,
     );
+    // Write to storage API (to selected user namespace).
+    var storageClient = ref.read(storageClientProvider);
+    final userClient = ref.read(usersProvider);
+    try {
+      await storageClient.write(storage_api.KeyValue(
+        key: VSSPath.vehicleHmiPressureUnit,
+        value: unit == PressureUnit.kilopascals ? "KPA" : "PSI",
+        namespace: userClient.selectedUser.id
+      ));
+    } catch (e) {
+      print('Error saving distance unit: $e');
+    }
   }
 }
index 8b48382..c2755f6 100644 (file)
@@ -4,9 +4,65 @@ import 'package:uuid/uuid.dart';
 
 import '../models/user.dart';
 
-class UsersNotifier extends StateNotifier<Users> {
-  UsersNotifier(super.state) {
+import 'package:protos/storage-api.dart' as storage_api;
+import 'initialize_settings.dart';
+
+class UsersNotifier extends Notifier<Users> {
+  @override
+  Users build() {
+    // Initialize default state.
+    state = Users.initial();
     loadUsers();
+    return state;
+  }
+
+  Future <void> loadSettingsUsers() async {
+    final storageClient = ref.read(storageClientProvider);
+    try {
+      // Access users branch.
+      final searchResponseUsers = await storageClient.search(storage_api.Key(key: UsersPath.InfotainmentUsers));
+      // Add default users if no users are inside the storage API.
+      if (searchResponseUsers.result.isEmpty) {
+        loadUsers();
+        await storageClient.write(storage_api.KeyValue(key: '${UsersPath.InfotainmentUsers}.${_users[0].id}.id', value: _users[0].id));
+        await storageClient.write(storage_api.KeyValue(key: '${UsersPath.InfotainmentUsers}.${_users[0].id}.name', value: _users[0].name));
+        await storageClient.write(storage_api.KeyValue(key: '${UsersPath.InfotainmentUsers}.${_users[1].id}.id', value: _users[1].id));
+        await storageClient.write(storage_api.KeyValue(key: '${UsersPath.InfotainmentUsers}.${_users[1].id}.name', value: _users[1].name));
+        await storageClient.write(storage_api.KeyValue(key: '${UsersPath.InfotainmentUsers}.${_users[2].id}.id', value: _users[2].id));
+        await storageClient.write(storage_api.KeyValue(key: '${UsersPath.InfotainmentUsers}.${_users[2].id}.name', value: _users[2].name));
+        await selectUser(_users[0].id);
+      }
+      else {
+        List<User> users = [];
+        List<String> idList = [];
+        // Get list of all ids.
+        for (var key in searchResponseUsers.result) {
+          var readResponse = await storageClient.read(storage_api.Key(key: key));
+          if (key.contains('.id')) {
+            idList.insert(0, readResponse.result);
+          }
+        }
+        // Extract names corresponding to ids.
+        for (var id in idList) {
+          var readResponse = await storageClient.read(storage_api.Key(key:'${UsersPath.InfotainmentUsers}.$id.name'));
+          users.insert(0, User(id: id, name: readResponse.result));
+        }
+        // Extract id of selected user.
+        final readResponseSelectedUser = await storageClient.read(storage_api.Key(key: UsersPath.InfotainmentCurrentUser));
+        User selectedUser;
+        final userCurrentId = readResponseSelectedUser.result;
+        // Extract name of selected user.
+        final readResponseCurrentUserName = await storageClient.read(storage_api.Key(key: '${UsersPath.InfotainmentUsers}.$userCurrentId.name'));
+        final userCurrentName = readResponseCurrentUserName.result;
+        selectedUser = User(id: userCurrentId, name: userCurrentName);
+        state =  Users(users: users, selectedUser: selectedUser);
+      }
+    } catch (e) {
+        // Fallback to initial defaults if error.
+        print('Error loading settings for units: $e');
+        loadUsers();
+        state = state.copyWith(selectedUser: _users[0]);
+    }
   }
 
   void loadUsers() {
@@ -18,27 +74,81 @@ class UsersNotifier extends StateNotifier<Users> {
     const User(id: '2', name: 'George'),
     const User(id: '3', name: 'Riley'),
   ];
-  void selectUser(String userId) {
+  
+  Future <void> selectUser(String userId) async {
+    final storageClient = ref.read(storageClientProvider);
     var seletedUser = state.users.firstWhere((user) => user.id == userId);
     state = state.copyWith(selectedUser: seletedUser);
+    // Write to storage API.
+    try {
+      await storageClient.write(storage_api.KeyValue(
+        key: UsersPath.InfotainmentCurrentUser,
+        value: userId,
+      ));
+    } catch (e) {
+      print('Error saving user: $e');
+    }
+    
+    try {
+      await initializeSettingsUser(ref);
+    } catch (e) {
+      print('Error loading settings of user: $e');
+    }
+    
   }
 
-  void removeUser(String userId) {
+  Future <void> removeUser(String userId) async {
+    final storageClient = ref.read(storageClientProvider);
+    var currentUserId = state.selectedUser.id;
     state.users.removeWhere((user) => user.id == userId);
-    if (state.users.isNotEmpty) {
+
+    if (state.users.isNotEmpty && currentUserId == userId) {
       state = state.copyWith(selectedUser: state.users.first);
+      //Write to API to change selected user.
+      await storageClient.write(storage_api.KeyValue(key: UsersPath.InfotainmentCurrentUser, value: state.users.first.id));
     }
     if (state.users.isEmpty) {
-      state = state.copyWith(selectedUser: const User(id: '', name: ''));
+      state = state.copyWith(selectedUser: const User(id: '0', name: ''));
+      //Write to API to change selected user.
+      await storageClient.write(storage_api.KeyValue(key: UsersPath.InfotainmentCurrentUser, value: '0'));
+    }
+    // Delete from storage API.
+    try {
+      final searchResponse = await storageClient.search(storage_api.Key(key: userId));
+      final keyList = searchResponse.result;
+      //Delete id, name entries of the user from the default namespace.
+      for (final key in keyList) {
+        await storageClient.delete(storage_api.Key(
+        key: key
+        ));
+      }
+      //Delete all VSS keys from the user namespace.
+      await storageClient.deleteNodes(storage_api.Key(key: "Vehicle", namespace: userId));
+    } catch (e) {
+      print('Error removing user with id $userId: $e');
     }
   }
 
-  void addUser(String userName) {
+  Future <void> addUser(String userName) async {
+    final storageClient = ref.read(storageClientProvider);
     final id = const Uuid().v1();
     final user = User(id: id, name: userName);
-
     state.users.insert(0, user);
-    state = state.copyWith(selectedUser: state.users.first);
+    // New user is automaticaly selected.
+    await selectUser(user.id); 
+    // Write to storage API.
+    try {
+      await storageClient.write(storage_api.KeyValue(
+        key: '${UsersPath.InfotainmentUsers}.$id.name',
+        value: userName
+      ));
+      await storageClient.write(storage_api.KeyValue(
+        key: '${UsersPath.InfotainmentUsers}.$id.id',
+        value: id
+      ));
+    } catch (e) {
+      print('Error adding user with id $id: $e');
+    }
   }
 
   void editUser(User user) {
index 9b4d027..0bb4070 100644 (file)
@@ -16,7 +16,7 @@ class Users {
   Users.initial()
       //: users = <User>[],
       : users = [],
-        selectedUser = const User(id: '', name: '');
+        selectedUser = const User(id: '0', name: '');
 
   Users copyWith({
     List<User>? users,
index 90ed196..be4026f 100644 (file)
@@ -60,6 +60,7 @@ export 'presentation/screens/clock/clock.dart';
 
 export 'core/utils/widgets/back_button.dart';
 export 'core/constants/vss_path.dart';
+export 'core/constants/users_path.dart';
 export 'core/constants/constants.dart';
 //Common widgets
 export 'presentation/common_widget/settings_top_bar.dart';
index c7d84ee..0699f06 100644 (file)
@@ -1,14 +1,27 @@
 import 'package:device_preview/device_preview.dart';
 
 import 'export.dart';
+import 'data/data_providers/initialize_settings.dart';
 
 void main() async {
   WidgetsFlutterBinding.ensureInitialized();
-  runApp(DevicePreview(
-    enabled: debugDisplay,
-    tools: const [
-      ...DevicePreview.defaultTools,
-    ],
-    builder: (context) => const App(),
-  ));
+
+  // Initialize settings from storage API.
+  final container = ProviderContainer();
+  await initializeSettings(container);
+
+  // Pass the container to ProviderScope and then run the app.
+  runApp(
+    ProviderScope(
+      parent: container,
+      child: DevicePreview(
+        enabled: debugDisplay,
+        tools: const [
+          ...DevicePreview.defaultTools,
+        ],
+        builder: (context) => const App(),
+      ),
+    ),
+  );
 }
+
diff --git a/test/storageAPI_UnitsForUsers_test.dart b/test/storageAPI_UnitsForUsers_test.dart
new file mode 100644 (file)
index 0000000..c4bdb14
--- /dev/null
@@ -0,0 +1,286 @@
+import 'package:flutter_test/flutter_test.dart';
+import 'package:protos/storage-api.dart' as storage_api;
+import 'package:flutter_ics_homescreen/export.dart';
+
+import 'package:flutter_ics_homescreen/data/data_providers/storage_client.dart';
+import 'package:flutter_ics_homescreen/data/data_providers/initialize_settings.dart';
+
+// Mock implementation of Ref if necessary.
+class MockRef extends Ref {
+  @override
+  dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
+}
+
+void main() {
+  late StorageClient storageClient;
+  late ProviderContainer container;
+  
+  setUp(() {
+    storageClient = StorageClient(
+      config: StorageConfig.defaultConfig(),
+      ref: MockRef(),
+    );
+    container = ProviderContainer();
+    // Dispose container after each test.
+    addTearDown(container.dispose);
+  });
+
+  test('add User', () async {
+    await storageClient.destroyDB();
+    final userClient = container.read(usersProvider.notifier);
+    await userClient.addUser('Mark');
+    // Access state.
+    var userState = container.read(usersProvider);
+    final userId = userState.users[0].id;
+    final searchResponse = await storageClient.read(storage_api.Key(key: '${UsersPath.InfotainmentUsers}.$userId.name'));
+    expect(searchResponse.success, isTrue);
+    expect(searchResponse.result, 'Mark');
+    await storageClient.destroyDB();
+  });
+
+  test('remove User', () async {
+    await storageClient.destroyDB();
+    // Add User.
+    final userClient = container.read(usersProvider.notifier);
+    await userClient.addUser('Mark');
+    // Access state.
+    var userState = container.read(usersProvider);
+    final markId = userState.users[0].id;
+
+    // Remove User.
+    await userClient.removeUser(markId);
+    final searchResponse = await storageClient.search(storage_api.Key(key: markId));
+    expect(searchResponse.success, isTrue);
+    expect(searchResponse.result, []);
+    await storageClient.destroyDB();
+  });
+
+  test('add users, select user', () async {
+    await storageClient.destroyDB();
+    // Add Users.
+    final userClient = container.read(usersProvider.notifier);
+    await userClient.addUser('Mark');
+    await userClient.addUser('Clara');
+
+    var userState = container.read(usersProvider);
+    final markId = userState.users[1].id;
+    await userClient.selectUser(markId);
+
+    final readResponseAfterSelectUser = await storageClient.read(storage_api.Key(key: UsersPath.InfotainmentCurrentUser));
+    expect(readResponseAfterSelectUser.success, isTrue);
+    expect(readResponseAfterSelectUser.result, markId);
+    await storageClient.destroyDB();
+  });
+
+    test('selected user default', () async {
+    await storageClient.destroyDB();
+
+    // Access state.
+    var userState = container.read(usersProvider);
+    final selectedId = userState.selectedUser.id;
+
+    final readResponse = await storageClient.read(storage_api.Key(key: UsersPath.InfotainmentCurrentUser));
+    expect(readResponse.success, isFalse);
+    expect(selectedId, '0');
+    await storageClient.destroyDB();
+  });
+
+  test('save Unit preference for a User', () async {
+    await storageClient.destroyDB();
+    // Add Users.
+    final userClient = container.read(usersProvider.notifier);
+    final unitsClient = container.read(unitStateProvider.notifier);
+    await userClient.addUser('Mark');
+    await userClient.addUser('Clara');
+
+    var userState = container.read(usersProvider);
+    final markId = userState.users[1].id;
+    await userClient.selectUser(markId);
+
+    await unitsClient.setDistanceUnit(DistanceUnit.miles);
+
+    final readResponse = await storageClient.read(storage_api.Key(key: VSSPath.vehicleHmiDistanceUnit, namespace: markId));
+    expect(readResponse.success, isTrue);
+    expect(readResponse.result, 'MILES');
+    await storageClient.destroyDB();
+  });
+
+  test('load settings: add users, selcect user, setDistanceUnit, kill state,  initialize', () async {
+    await storageClient.destroyDB();
+
+    final userClient = container.read(usersProvider.notifier);
+    final unitsClient = container.read(unitStateProvider.notifier);
+
+    // Prepare state.
+    await userClient.addUser('Mark');
+    await userClient.addUser('Clara');
+    var userState = container.read(usersProvider);
+    final markId = userState.users[1].id;
+    await userClient.selectUser(markId);
+    await unitsClient.setDistanceUnit(DistanceUnit.miles);
+
+    // Check success.
+    var unitState = container.read(unitStateProvider);
+    userState = container.read(usersProvider);
+    expect(userState.users[0].name, 'Clara');
+    expect(userState.users[1].name, 'Mark');
+    expect(userState.users.length, 5);
+    expect(userState.selectedUser.id, markId);
+    expect(unitState.distanceUnit, DistanceUnit.miles);
+    
+    // Killing state.
+    container.dispose();
+    container = ProviderContainer();
+    addTearDown(container.dispose);
+
+    // Check that state is killed.
+    userState = container.read(usersProvider);
+    unitState = container.read(unitStateProvider);
+    expect(userState.selectedUser.id, '0');
+    expect(unitState.distanceUnit, DistanceUnit.kilometers);
+
+    // Load state.
+    await initializeSettings(container);
+    
+    // Check success.
+    unitState = container.read(unitStateProvider);
+    userState = container.read(usersProvider);
+    expect(userState.selectedUser.id, markId);
+    List<String> userNames = userState.users.map((user) => user.name).toList();
+    expect(userNames.contains('Mark'), isTrue);
+    expect(userNames.contains('Clara'), isTrue);
+    expect(userState.users.length, 2);
+    expect(unitState.distanceUnit, DistanceUnit.miles);
+  
+    await storageClient.destroyDB();
+  });
+
+  test('loadsettings: add users, setDistanceUnit, kill state, initialize', () async {
+    await storageClient.destroyDB();
+
+    final userClient = container.read(usersProvider.notifier);
+    final unitsClient = container.read(unitStateProvider.notifier);
+
+    // Prepare state.
+    await userClient.addUser('Mark');
+    await userClient.addUser('Clara');
+    var userState = container.read(usersProvider);
+    final claraId = userState.users[0].id;
+    await unitsClient.setDistanceUnit(DistanceUnit.miles);
+
+    // Check success.
+    var unitState = container.read(unitStateProvider);
+    userState = container.read(usersProvider);
+    expect(userState.users[0].name, 'Clara');
+    expect(userState.users[1].name, 'Mark');
+    expect(userState.selectedUser.id, claraId);
+    expect(unitState.distanceUnit, DistanceUnit.miles);
+    
+    // Killing state.
+    container.dispose();
+    container = ProviderContainer();
+    addTearDown(container.dispose);
+
+    // Check that state is killed.
+    userState = container.read(usersProvider);
+    unitState = container.read(unitStateProvider);
+    expect(userState.selectedUser.id, '0');
+    expect(unitState.distanceUnit, DistanceUnit.kilometers);
+
+    // Load state.
+    await initializeSettings(container);
+    
+    // Check success.
+    unitState = container.read(unitStateProvider);
+    userState = container.read(usersProvider);
+    expect(userState.selectedUser.id, claraId);
+    List<String> userNames = userState.users.map((user) => user.name).toList();
+    expect(userNames.contains('Mark'), isTrue);
+    expect(userNames.contains('Clara'), isTrue);
+    expect(unitState.distanceUnit, DistanceUnit.miles);
+  
+    await storageClient.destroyDB();
+  });
+
+  test('loadsettings: initialize, add no user, setDistanceUnit, kill state, inizialize', () async {
+    await storageClient.destroyDB();
+    await initializeSettings(container);
+
+    var userState = container.read(usersProvider);
+    var readResponse = await storageClient.read(storage_api.Key(key: '${UsersPath.InfotainmentUsers}.1.name'));
+    expect(readResponse.result, 'Heather');
+
+    final unitsClient = container.read(unitStateProvider.notifier);
+
+    // Prepare state.
+    userState = container.read(usersProvider);
+    await unitsClient.setDistanceUnit(DistanceUnit.miles);
+
+    // Check success.
+    var unitState = container.read(unitStateProvider);
+    userState = container.read(usersProvider);
+    expect(userState.users[0].name, 'Heather');
+    expect(unitState.distanceUnit, DistanceUnit.miles);
+    expect(userState.selectedUser.id, '1');
+    
+    // Killing state.
+    container.dispose();
+    container = ProviderContainer();
+    addTearDown(container.dispose);
+
+    // Check that state is killed.
+    userState = container.read(usersProvider);
+    unitState = container.read(unitStateProvider);
+    expect(userState.selectedUser.id, '0');
+    expect(unitState.distanceUnit, DistanceUnit.kilometers);
+
+    // Load state.
+    await initializeSettings(container);
+    
+    // Check success.
+    unitState = container.read(unitStateProvider);
+    userState = container.read(usersProvider);
+    expect(userState.users[0].name, 'Heather');
+    expect(unitState.distanceUnit, DistanceUnit.miles);
+    expect(userState.selectedUser.name, 'Heather');
+  
+    await storageClient.destroyDB();
+  });
+
+  test('select user: load settings', () async {
+    await storageClient.destroyDB();
+
+    final userClient = container.read(usersProvider.notifier);
+    final unitsClient = container.read(unitStateProvider.notifier);
+    // Prepare state.
+    await userClient.addUser('Mark');
+    await userClient.addUser('Clara');
+    var userState = container.read(usersProvider);
+    final claraId = userState.users[0].id;
+    final markId = userState.users[1].id;
+    // Note: current user is Clara.
+    await unitsClient.setDistanceUnit(DistanceUnit.miles);
+    await unitsClient.setTemperatureUnit(TemperatureUnit.fahrenheit);
+    await unitsClient.setPressureUnit(PressureUnit.psi);
+  
+    await userClient.selectUser(markId);
+    await unitsClient.setDistanceUnit(DistanceUnit.miles);
+    await unitsClient.setTemperatureUnit(TemperatureUnit.celsius);
+    await unitsClient.setPressureUnit(PressureUnit.kilopascals);
+  
+    await userClient.selectUser(claraId);
+    var unitState = container.read(unitStateProvider);
+    expect(unitState.distanceUnit, DistanceUnit.miles);
+    expect(unitState.temperatureUnit, TemperatureUnit.fahrenheit);
+    expect(unitState.pressureUnit, PressureUnit.psi);
+  
+    var readResponse = await storageClient.read(storage_api.Key(key: VSSPath.vehicleHmiDistanceUnit, namespace: claraId));
+    expect(readResponse.result, 'MILES');
+    readResponse = await storageClient.read(storage_api.Key(key: VSSPath.vehicleHmiTemperatureUnit, namespace: claraId));
+    expect(readResponse.result, 'F');
+    readResponse = await storageClient.read(storage_api.Key(key: VSSPath. vehicleHmiPressureUnit, namespace: claraId));
+    expect(readResponse.result, 'PSI');
+  
+    await storageClient.destroyDB();
+  });
+}