feat: add a headless entry point that runs a test with no UI
Separation that nothing exercises is separation that quietly stops being true, so this runs a full test through Iperf3Service and Iperf3Session with WidgetsFlutterBinding but no runApp: no widget tree, no render surface. If the run path ever starts needing the UI, this stops working. An architecture test additionally forbids it importing lib/ui. It is also the accurate way to measure. A 4-stream run against a local server reports 30.2% local CPU here, against 79.9% for a comparable run with the transcript repainting alongside — the UI was costing roughly fifty points of CPU on a phone already near CPU-bound at gigabit rates. Throughput figures taken through the UI are therefore pessimistic, and this is the path to use when the number matters. Parameters come from --dart-define so a run needs no code edit: flutter run -t lib/main_headless.dart --dart-define=IPERF_SERVER=… Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -82,6 +82,22 @@ void main() {
|
||||
);
|
||||
});
|
||||
|
||||
test('the headless entry point does not depend on ui', () {
|
||||
// main_headless.dart is the standing proof that the core runs without a
|
||||
// widget tree; an import from lib/ui would quietly end that.
|
||||
final offenders = _importsMatching(
|
||||
'lib',
|
||||
(import) => import.contains('/ui/'),
|
||||
onlyFiles: {'lib/main_headless.dart'},
|
||||
);
|
||||
expect(
|
||||
offenders,
|
||||
isEmpty,
|
||||
reason: 'The headless entry point must run the core with no UI.\n'
|
||||
'${_render(offenders)}',
|
||||
);
|
||||
});
|
||||
|
||||
test('every layer directory is actually covered by these rules', () {
|
||||
// Guards against a new top-level layer appearing and silently escaping
|
||||
// every rule above.
|
||||
@@ -102,8 +118,9 @@ void main() {
|
||||
/// Every `(file, import)` pair under [directory] whose import satisfies [test].
|
||||
List<({String file, String import})> _importsMatching(
|
||||
String directory,
|
||||
bool Function(String import) test,
|
||||
) {
|
||||
bool Function(String import) test, {
|
||||
Set<String>? onlyFiles,
|
||||
}) {
|
||||
final results = <({String file, String import})>[];
|
||||
final dir = Directory(directory);
|
||||
if (!dir.existsSync()) return results;
|
||||
@@ -111,6 +128,7 @@ List<({String file, String import})> _importsMatching(
|
||||
final pattern = RegExp(r'''^\s*import\s+['"]([^'"]+)['"]''', multiLine: true);
|
||||
for (final entity in dir.listSync(recursive: true)) {
|
||||
if (entity is! File || !entity.path.endsWith('.dart')) continue;
|
||||
if (onlyFiles != null && !onlyFiles.contains(entity.path)) continue;
|
||||
final source = entity.readAsStringSync();
|
||||
for (final match in pattern.allMatches(source)) {
|
||||
final import = match.group(1)!;
|
||||
|
||||
Reference in New Issue
Block a user