Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 15 additions & 6 deletions lib/page/dashboard/views/components/widgets/atomic/speed_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -444,24 +444,33 @@ class _HistoryChartPainter extends CustomPainter {
// Ideally we use uploadBandwidthKbps if available.
final downloads = data.map((e) {
if (e.downloadBandwidthKbps != null && e.downloadBandwidthKbps! > 0) {
return e.downloadBandwidthKbps! / 1024.0; // Mbps
return e.downloadBandwidthKbps! /
1000.0; // Mbps (SI: 1 Mbps = 1000 kbps)
}
// Fallback to parsing string value
final speed = double.tryParse(e.downloadSpeed) ?? 0;
if (e.downloadUnit.toUpperCase() == 'KBPS') {
return speed / 1024.0;
final unit = e.downloadUnit.toLowerCase();
if (unit == 'kb' || unit == 'kbps') {
return speed / 1000.0;
}
if (unit == 'gb' || unit == 'gbps') {
return speed * 1000.0;
}
return speed; // Assume Mbps
}).toList();

final uploads = data.map((e) {
if (e.uploadBandwidthKbps != null && e.uploadBandwidthKbps! > 0) {
return e.uploadBandwidthKbps! / 1024.0; // Mbps
return e.uploadBandwidthKbps! / 1000.0; // Mbps (SI: 1 Mbps = 1000 kbps)
}
// Fallback to parsing string value
final speed = double.tryParse(e.uploadSpeed) ?? 0;
if (e.uploadUnit.toUpperCase() == 'KBPS') {
return speed / 1024.0;
final unit = e.uploadUnit.toLowerCase();
if (unit == 'kb' || unit == 'kbps') {
return speed / 1000.0;
}
if (unit == 'gb' || unit == 'gbps') {
return speed * 1000.0;
}
return speed; // Assume Mbps
}).toList();
Expand Down
2 changes: 1 addition & 1 deletion lib/page/health_check/providers/health_check_provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ class HealthCheckProvider extends Notifier<HealthCheckState> {
});
} else {
// Add a random value to simulate a fluctuating meter during tests.
final randomValue = (_random.nextDouble() * (15 - (-3)) + (-3)) * 1024;
final randomValue = (_random.nextDouble() * (15 - (-3)) + (-3)) * 1000;
meterValue += randomValue;
}

Expand Down
4 changes: 2 additions & 2 deletions lib/page/health_check/services/health_check_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -186,11 +186,11 @@ class SpeedTestService {
return SpeedTestUIModel.empty();
}
final download = NetworkUtils.formatBitsWithUnit(
(speedTestResult.downloadBandwidth ?? 0) * 1024,
(speedTestResult.downloadBandwidth ?? 0) * 1000,
decimals: 1,
);
final upload = NetworkUtils.formatBitsWithUnit(
(speedTestResult.uploadBandwidth ?? 0) * 1024,
(speedTestResult.uploadBandwidth ?? 0) * 1000,
decimals: 1,
);
Comment on lines 188 to 195

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. Chart uses 1024 base 🐞 Bug ✓ Correctness

After this PR’s switch to base-1000 conversions for speed-test bandwidth, the dashboard history
chart still divides Kbps by 1024 to get Mbps and uses a brittle unit-string fallback. This will make
historical chart values inconsistent with the live speed-test UI and can be plainly wrong when the
raw Kbps fields are absent/zero.
Agent Prompt
### Issue description
The speed test UI now treats bandwidth as SI (base-1000) (e.g., Kbps→bps uses `* 1000`, and `NetworkUtils.formatBitsWithUnit` uses base-1000 and returns `unit` like `kb`). The dashboard history chart still converts Kbps→Mbps using `/ 1024.0` and only converts parsed values when `unit.toUpperCase() == 'KBPS'`, which does not match the unit strings actually stored/produced.

### Issue Context
This causes historical chart values to diverge from the live speed-test UI. It can also produce plainly wrong values in the fallback branch when `downloadBandwidthKbps/uploadBandwidthKbps` are null/0, because the chart may skip conversion and “assume Mbps”.

### Fix Focus Areas
- lib/page/dashboard/views/components/widgets/atomic/speed_test.dart[438-467]
- lib/utils.dart[330-352]
- lib/page/health_check/services/health_check_service.dart[176-209]

### Suggested fix
1. In the chart, change Kbps→Mbps conversion to divide by `1000.0` (not `1024.0`) for both download and upload.
2. Replace the `unit == 'KBPS'` fallback with logic that recognizes the actual units used by the app (case-insensitive):
   - `kb` / `kbps` => divide by 1000
   - `mb` / `mbps` => no change
   - `gb` / `gbps` => multiply by 1000
   - (optionally extend for Tb/Pb)
3. Consider centralizing this conversion into a small helper (e.g., `double toMbps(double value, String unit)`), and add a unit test for it.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

final (formattedTimestamp, _) = _formatTimestamp(timestamp ?? '');
Expand Down
4 changes: 2 additions & 2 deletions lib/page/health_check/shared_widgets/speed_test_widget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -225,13 +225,13 @@ class SpeedTestWidget extends ConsumerWidget {
final result = state.result ?? SpeedTestUIModel.empty();
// Format the live meter value for display.
final formattedLiveValue = NetworkUtils.formatBitsWithUnit(
(state.meterValue * 1024).toInt(),
(state.meterValue * 1000).toInt(),
decimals: 1);

final bandwidthValue = formattedLiveValue.value;
final bandwidthUnit = formattedLiveValue.unit;

final meterValueMbps = (state.meterValue / 1024);
final meterValueMbps = (state.meterValue / 1000);

return Center(
child: AppGauge(
Expand Down
4 changes: 2 additions & 2 deletions lib/utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,7 @@ extension MediaQueryUtils on Utils {
extension NetworkUtils on Utils {
/// Formats a bit count into a human-readable string with SI units (base 1000).
///
/// Example: 1000 bits -> "1 Kb"
/// Example: 1000 bits -> "1 kb"
static String formatBits(int bits, {int decimals = 0}) {
final result = formatBitsWithUnit(bits, decimals: decimals);
return '${result.value} ${result.unit}';
Expand All @@ -342,7 +342,7 @@ extension NetworkUtils on Utils {
static ({String value, String unit}) formatBitsWithUnit(int bits,
{int decimals = 0}) {
if (bits <= 0) return (value: '0', unit: "b");
const suffixes = ["b", "Kb", "Mb", "Gb", "Tb", "Pb"];
const suffixes = ["b", "kb", "Mb", "Gb", "Tb", "Pb"];
var i = (log(bits) / log(1000)).floor();
var number = (bits / pow(1000, i));
return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ void main() {
expect(state.result, partialResult);
expect(state.step, HealthCheckStep.uploadBandwidth);
// Meter value should have changed from 0.0 (initial) to a predictable non-zero value
const expectedRandomValue = (0.5 * (15 - (-3)) + (-3)) * 1024;
const expectedRandomValue = (0.5 * (15 - (-3)) + (-3)) * 1000;
expect(state.meterValue, expectedRandomValue);
});

Expand Down
8 changes: 4 additions & 4 deletions test/utils_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -510,7 +510,7 @@ void main() {
test('formatBits: formats bits in kilobytes range with specified decimals',
() {
const bits = 1234;
const expected = '1.234 Kb';
const expected = '1.234 kb';

final formattedBits = NetworkUtils.formatBits(bits, decimals: 3);
expect(formattedBits, expected);
Expand Down Expand Up @@ -565,18 +565,18 @@ void main() {
expect(result.unit, 'b');
});

test('formats bits (less than 1Kb)', () {
test('formats bits (less than 1kb)', () {
const bits = 500;
final result = NetworkUtils.formatBitsWithUnit(bits);
expect(result.value, '500');
expect(result.unit, 'b');
});

test('formats kilobytes with 0 decimal places', () {
const bits = 2000; // 2 Kb
const bits = 2000; // 2 kb
final result = NetworkUtils.formatBitsWithUnit(bits);
expect(result.value, '2');
expect(result.unit, 'Kb');
expect(result.unit, 'kb');
});

test('formats megabytes with 2 decimal places', () {
Expand Down