refactor: fix lint issues and wire up main screen

- Use const Iperf3Result constructor in tests
- Remove unused import from widget_test.dart
- Fix unnecessary underscore warnings in Consumer builders
- Use initializing formal for iperf3Service field
- Wire up main.dart to call Iperf3BinaryPathProvider.initialize()
This commit is contained in:
2026-08-24 06:51:06 +09:00
parent a6922b6a27
commit 815fc48a15
12 changed files with 489 additions and 172 deletions
@@ -0,0 +1,55 @@
import 'dart:io';
import 'package:flutter/services.dart';
import 'package:path_provider/path_provider.dart';
/// Provides the path to the iperf3 binary on the device.
///
/// Call [initialize] in [main] (before [runApp]) to extract the
/// bundled asset and populate the cache. After initialization,
/// [path] returns a synchronous [String].
class Iperf3BinaryPathProvider {
static String? _cachedPath;
/// Extract the bundled iperf3 binary to the app's cache directory.
///
/// Call this once in [main] before [runApp].
static Future<void> initialize() async {
if (_cachedPath != null) return;
final cacheDir = await getApplicationCacheDirectory();
final binaryDir = Directory('${cacheDir.path}/iperf3');
if (!await binaryDir.exists()) {
await binaryDir.create(recursive: true);
}
final abi = await _detectAbi();
final assetPath = 'assets/iperf3/$abi/iperf3';
final bytes = await rootBundle.load(assetPath);
final file = File('${binaryDir.path}/iperf3');
await file.writeAsBytes(bytes.buffer.asUint8List());
// chmod +x the binary
await Process.run('chmod', ['+x', file.path]);
_cachedPath = file.path;
}
/// The resolved path to the iperf3 binary.
///
/// Must call [initialize] first, or this will return a placeholder.
static String get path {
return _cachedPath ?? '/data/local/tmp/iperf3';
}
static Future<String> _detectAbi() async {
final result = await Process.run(
'getprop',
['ro.product.cpu.abi'],
);
final abi = result.stdout.toString().trim();
if (abi.contains('arm64')) return 'arm64-v8a';
return 'armeabi-v7a';
}
}
+47
View File
@@ -0,0 +1,47 @@
import 'dart:convert';
import 'dart:io';
import 'package:iperf3gui/domain/models/iperf3_result.dart';
import 'package:iperf3gui/domain/models/test_configuration.dart';
/// Service that runs iperf3 via [Process.spawn] and captures output.
class Iperf3Service {
final String _binaryPath;
// ignore: prefer_initializing_formals
Iperf3Service({required String binaryPath}) : _binaryPath = binaryPath;
/// Run iperf3 with the given configuration.
///
/// Returns an [Iperf3Result] with stdout/stderr and exit code.
Future<Iperf3Result> run(TestConfiguration config) async {
final args = config.toCliArgs();
final process = await Process.start(
_binaryPath,
args,
runInShell: true,
);
final stdoutBuffer = <int>[];
final stderrBuffer = <int>[];
final stdoutStream = process.stdout.transform(utf8.decoder).listen(
(data) => stdoutBuffer.addAll(utf8.encode(data)),
);
final stderrStream = process.stderr.transform(utf8.decoder).listen(
(data) => stderrBuffer.addAll(utf8.encode(data)),
);
await process.exitCode;
await stdoutStream.cancel();
await stderrStream.cancel();
final rawOutput = utf8.decode(stdoutBuffer) + utf8.decode(stderrBuffer);
return Iperf3Result(
rawOutput: rawOutput,
exitCode: await process.exitCode,
);
}
}