From 44a7ff7f3e13a87fea0542476aca6b2fb2fe27fd Mon Sep 17 00:00:00 2001 From: TobiasRoeddiger Date: Fri, 2 Oct 2026 11:38:02 +0200 Subject: [PATCH 1/3] fix(firmware): compare development versions using semantic precedence --- .../models/firmware_version_comparison.dart | 16 ++++++++++++ .../lib/view_models/wearables_provider.dart | 3 ++- .../firmware_version_comparison_test.dart | 26 +++++++++++++++++++ 3 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 open_wearable/lib/models/firmware_version_comparison.dart create mode 100644 open_wearable/test/models/firmware_version_comparison_test.dart 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..ef05599f2 --- /dev/null +++ b/open_wearable/lib/models/firmware_version_comparison.dart @@ -0,0 +1,16 @@ +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..8fef80fb5 --- /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); + }); +} From f2187b7be09d24fbce04d80cdf3306fb11593062 Mon Sep 17 00:00:00 2001 From: TobiasRoeddiger Date: Fri, 2 Oct 2026 11:53:22 +0200 Subject: [PATCH 2/3] style: satisfy firmware comparison lint --- open_wearable/lib/models/firmware_version_comparison.dart | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/open_wearable/lib/models/firmware_version_comparison.dart b/open_wearable/lib/models/firmware_version_comparison.dart index ef05599f2..683af69b0 100644 --- a/open_wearable/lib/models/firmware_version_comparison.dart +++ b/open_wearable/lib/models/firmware_version_comparison.dart @@ -8,8 +8,12 @@ bool isNewerFirmwareVersion(String latest, String current) { .trim() .replaceFirst(RegExp(r'^v(?=\d)'), ''); final version = Version.parse(normalized); - return Version(version.major, version.minor, version.patch, - pre: version.preRelease.join('.')); + return Version( + version.major, + version.minor, + version.patch, + pre: version.preRelease.join('.'), + ); } return parse(latest) > parse(current); From 884bf9de087b0a3fc88fb10381351777c43e9393 Mon Sep 17 00:00:00 2001 From: TobiasRoeddiger Date: Fri, 2 Oct 2026 12:18:54 +0200 Subject: [PATCH 3/3] style: satisfy regression test lint --- .../test/models/firmware_version_comparison_test.dart | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/open_wearable/test/models/firmware_version_comparison_test.dart b/open_wearable/test/models/firmware_version_comparison_test.dart index 8fef80fb5..0b04624b5 100644 --- a/open_wearable/test/models/firmware_version_comparison_test.dart +++ b/open_wearable/test/models/firmware_version_comparison_test.dart @@ -5,11 +5,11 @@ void main() { test('compares released firmware against the installed development build', () { expect( - isNewerFirmwareVersion('2.2.9', '2.2.10-dev.60+ga99d057fa'), isFalse); + isNewerFirmwareVersion('2.2.9', '2.2.10-dev.60+ga99d057fa'), isFalse,); expect( - isNewerFirmwareVersion('2.2.10', '2.2.10-dev.60+ga99d057fa'), isTrue); + isNewerFirmwareVersion('2.2.10', '2.2.10-dev.60+ga99d057fa'), isTrue,); expect( - isNewerFirmwareVersion('2.2.11', '2.2.10-dev.60+ga99d057fa'), isTrue); + isNewerFirmwareVersion('2.2.11', '2.2.10-dev.60+ga99d057fa'), isTrue,); }); test('prerelease numbers are numeric and build metadata has no precedence', () { @@ -21,6 +21,6 @@ void main() { test('unrecognized labels fail explicitly instead of suggesting a downgrade', () { expect(() => isNewerFirmwareVersion('2.2.9', 'PR #123'), - throwsFormatException); + throwsFormatException,); }); }