diff --git a/open_wearable/lib/models/firmware_version_comparison.dart b/open_wearable/lib/models/firmware_version_comparison.dart new file mode 100644 index 000000000..683af69b0 --- /dev/null +++ b/open_wearable/lib/models/firmware_version_comparison.dart @@ -0,0 +1,20 @@ +import 'package:pub_semver/pub_semver.dart'; + +/// Compare firmware precedence, ignoring transport padding and build metadata. +bool isNewerFirmwareVersion(String latest, String current) { + Version parse(String value) { + final normalized = value + .replaceAll('\x00', '') + .trim() + .replaceFirst(RegExp(r'^v(?=\d)'), ''); + final version = Version.parse(normalized); + return Version( + version.major, + version.minor, + version.patch, + pre: version.preRelease.join('.'), + ); + } + + return parse(latest) > parse(current); +} diff --git a/open_wearable/lib/view_models/wearables_provider.dart b/open_wearable/lib/view_models/wearables_provider.dart index c59a0d677..86277cd9f 100644 --- a/open_wearable/lib/view_models/wearables_provider.dart +++ b/open_wearable/lib/view_models/wearables_provider.dart @@ -8,6 +8,7 @@ import 'package:open_wearable/models/wearable_status_cache.dart'; import 'package:open_wearable/view_models/sensor_configuration_provider.dart'; import '../models/logger.dart'; +import '../models/firmware_version_comparison.dart'; /// Event emitted when a newer firmware version is available for a wearable. class NewFirmwareAvailableEvent extends WearableEvent { @@ -452,7 +453,7 @@ class WearablesProvider with ChangeNotifier { .getLatestFirmwareVersion() .then((version) => version.toString()); - if (firmwareImageRepository.isNewerVersion( + if (isNewerFirmwareVersion( latestVersion, currentVersion, )) { diff --git a/open_wearable/test/models/firmware_version_comparison_test.dart b/open_wearable/test/models/firmware_version_comparison_test.dart new file mode 100644 index 000000000..0b04624b5 --- /dev/null +++ b/open_wearable/test/models/firmware_version_comparison_test.dart @@ -0,0 +1,26 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:open_wearable/models/firmware_version_comparison.dart'; + +void main() { + test('compares released firmware against the installed development build', + () { + expect( + isNewerFirmwareVersion('2.2.9', '2.2.10-dev.60+ga99d057fa'), isFalse,); + expect( + isNewerFirmwareVersion('2.2.10', '2.2.10-dev.60+ga99d057fa'), isTrue,); + expect( + isNewerFirmwareVersion('2.2.11', '2.2.10-dev.60+ga99d057fa'), isTrue,); + }); + test('prerelease numbers are numeric and build metadata has no precedence', + () { + expect(isNewerFirmwareVersion('2.2.10-dev.10', '2.2.10-dev.9'), isTrue); + expect(isNewerFirmwareVersion('2.2.10+z', '2.2.10+a'), isFalse); + expect(isNewerFirmwareVersion(' v2.2.10\x00 ', '2.2.9'), isTrue); + expect(isNewerFirmwareVersion('2.2.9', '2.2.9'), isFalse); + }); + test('unrecognized labels fail explicitly instead of suggesting a downgrade', + () { + expect(() => isNewerFirmwareVersion('2.2.9', 'PR #123'), + throwsFormatException,); + }); +}